Skip to main content

mockforge_import/import/
postman_environment.rs

1//! Postman Environment import functionality
2//!
3//! This module handles parsing Postman environment files and extracting
4//! variables that can be used in MockForge templates and request chaining.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// Postman Environment structure from JSON files
10#[derive(Debug, Deserialize)]
11pub struct PostmanEnvironment {
12    /// Environment ID (if specified)
13    pub id: Option<String>,
14    /// Environment name
15    pub name: Option<String>,
16    /// Array of environment variables
17    #[serde(default)]
18    pub values: Vec<EnvironmentValue>,
19}
20
21/// Environment variable value from Postman
22#[derive(Debug, Deserialize)]
23pub struct EnvironmentValue {
24    /// Variable key/name
25    pub key: String,
26    /// Variable value (optional)
27    pub value: Option<String>,
28    /// Optional description of the variable
29    pub description: Option<String>,
30    /// Whether this variable is enabled
31    #[serde(default = "default_enabled")]
32    pub enabled: bool,
33}
34
35/// Result of importing a Postman environment
36#[derive(Debug)]
37pub struct EnvironmentImportResult {
38    /// Name of the imported environment
39    pub name: String,
40    /// Map of variable names to their values and metadata
41    pub variables: HashMap<String, EnvironmentVariable>,
42    /// Number of enabled variables imported
43    pub enabled_count: usize,
44    /// Total number of variables found (enabled + disabled)
45    pub total_count: usize,
46}
47
48/// Environment variable with metadata for use in MockForge
49#[derive(Debug, Serialize, Clone)]
50pub struct EnvironmentVariable {
51    /// Variable value
52    pub value: String,
53    /// Optional description from Postman
54    pub description: Option<String>,
55    /// Whether this variable is enabled
56    pub enabled: bool,
57    /// Source/origin of this variable
58    pub source: VariableSource,
59}
60
61/// Source/origin of an environment variable
62#[derive(Debug, Serialize, Clone)]
63pub enum VariableSource {
64    /// Variable from a named environment
65    Environment(String),
66    /// Variable from collection-level variables
67    Collection,
68}
69
70fn default_enabled() -> bool {
71    true
72}
73
74/// Import a Postman Environment JSON
75pub fn import_postman_environment(content: &str) -> Result<EnvironmentImportResult, String> {
76    let environment: PostmanEnvironment = serde_json::from_str(content)
77        .map_err(|e| format!("Failed to parse Postman environment: {}", e))?;
78
79    let mut variables = HashMap::new();
80    let mut enabled_count = 0;
81    let mut total_count = 0;
82
83    let env_name = environment.name.unwrap_or_else(|| "Unnamed Environment".to_string());
84
85    for env_value in environment.values {
86        total_count += 1;
87
88        if env_value.enabled {
89            if let Some(value) = env_value.value {
90                enabled_count += 1;
91                let variable = EnvironmentVariable {
92                    value,
93                    description: env_value.description,
94                    enabled: env_value.enabled,
95                    source: VariableSource::Environment(env_name.clone()),
96                };
97                variables.insert(env_value.key, variable);
98            }
99        }
100    }
101
102    Ok(EnvironmentImportResult {
103        name: env_name,
104        variables,
105        enabled_count,
106        total_count,
107    })
108}
109
110/// Check if content is a Postman environment JSON
111pub fn is_postman_environment_json(content: &str) -> bool {
112    if let Ok(json) = serde_json::from_str::<serde_json::Value>(content) {
113        if let Some(obj) = json.as_object() {
114            // Check for typical environment fields
115            let has_values = obj.contains_key("values");
116            let has_name_or_id = obj.contains_key("name") || obj.contains_key("id");
117
118            // values should be an array
119            let values_is_array = if let Some(values) = obj.get("values") {
120                values.is_array()
121            } else {
122                false
123            };
124
125            if has_values && has_name_or_id && values_is_array {
126                // Check if values array contains environment variable structure
127                if let Some(values_array) = obj.get("values") {
128                    if let Some(arr) = values_array.as_array() {
129                        if !arr.is_empty() {
130                            // Check if first item has typical environment variable fields
131                            if let Some(first_item) = arr.first() {
132                                if let Some(item_obj) = first_item.as_object() {
133                                    let has_key = item_obj.contains_key("key");
134                                    let has_value = item_obj.contains_key("value")
135                                        || item_obj.contains_key("enabled");
136                                    return has_key && has_value;
137                                }
138                            }
139                        }
140                    }
141                }
142            }
143        }
144    }
145    false
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151
152    #[test]
153    fn test_parse_postman_environment() {
154        let env_json = r#"{
155            "id": "env-123",
156            "name": "Development Environment",
157            "values": [
158                {
159                    "key": "base_url",
160                    "value": "https://api.dev.example.com",
161                    "description": "API base URL",
162                    "enabled": true
163                },
164                {
165                    "key": "api_key",
166                    "value": "dev-key-123",
167                    "enabled": true
168                },
169                {
170                    "key": "disabled_var",
171                    "value": "should_not_import",
172                    "enabled": false
173                }
174            ]
175        }"#;
176
177        let result = import_postman_environment(env_json).unwrap();
178
179        assert_eq!(result.name, "Development Environment");
180        assert_eq!(result.total_count, 3);
181        assert_eq!(result.enabled_count, 2);
182        assert_eq!(result.variables.len(), 2);
183
184        // Check base_url variable
185        let base_url_var = result.variables.get("base_url").unwrap();
186        assert_eq!(base_url_var.value, "https://api.dev.example.com");
187        assert_eq!(base_url_var.description.as_ref().unwrap(), "API base URL");
188        assert!(base_url_var.enabled);
189
190        // Check api_key variable
191        let api_key_var = result.variables.get("api_key").unwrap();
192        assert_eq!(api_key_var.value, "dev-key-123");
193        assert!(api_key_var.enabled);
194    }
195
196    #[test]
197    fn test_detect_postman_environment() {
198        let env_json = r#"{
199            "id": "env-123",
200            "name": "Test Environment",
201            "values": [
202                {
203                    "key": "test_var",
204                    "value": "test_value",
205                    "enabled": true
206                }
207            ]
208        }"#;
209
210        assert!(is_postman_environment_json(env_json));
211
212        let not_env_json = r#"{
213            "info": {
214                "name": "Test Collection"
215            },
216            "item": []
217        }"#;
218
219        assert!(!is_postman_environment_json(not_env_json));
220    }
221
222    #[test]
223    fn test_parse_minimal_environment() {
224        let minimal_env_json = r#"{
225            "values": [
226                {
227                    "key": "minimal_var",
228                    "value": "minimal_value",
229                    "enabled": true
230                }
231            ]
232        }"#;
233
234        let result = import_postman_environment(minimal_env_json).unwrap();
235
236        assert_eq!(result.name, "Unnamed Environment");
237        assert_eq!(result.total_count, 1);
238        assert_eq!(result.enabled_count, 1);
239        assert_eq!(result.variables.len(), 1);
240    }
241}