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 && env_value.value.is_some() {
89            enabled_count += 1;
90            let variable = EnvironmentVariable {
91                value: env_value.value.unwrap(),
92                description: env_value.description,
93                enabled: env_value.enabled,
94                source: VariableSource::Environment(env_name.clone()),
95            };
96            variables.insert(env_value.key, variable);
97        }
98    }
99
100    Ok(EnvironmentImportResult {
101        name: env_name,
102        variables,
103        enabled_count,
104        total_count,
105    })
106}
107
108/// Check if content is a Postman environment JSON
109pub fn is_postman_environment_json(content: &str) -> bool {
110    if let Ok(json) = serde_json::from_str::<serde_json::Value>(content) {
111        if let Some(obj) = json.as_object() {
112            // Check for typical environment fields
113            let has_values = obj.contains_key("values");
114            let has_name_or_id = obj.contains_key("name") || obj.contains_key("id");
115
116            // values should be an array
117            let values_is_array = if let Some(values) = obj.get("values") {
118                values.is_array()
119            } else {
120                false
121            };
122
123            if has_values && has_name_or_id && values_is_array {
124                // Check if values array contains environment variable structure
125                if let Some(values_array) = obj.get("values") {
126                    if let Some(arr) = values_array.as_array() {
127                        if !arr.is_empty() {
128                            // Check if first item has typical environment variable fields
129                            if let Some(first_item) = arr.first() {
130                                if let Some(item_obj) = first_item.as_object() {
131                                    let has_key = item_obj.contains_key("key");
132                                    let has_value = item_obj.contains_key("value")
133                                        || item_obj.contains_key("enabled");
134                                    return has_key && has_value;
135                                }
136                            }
137                        }
138                    }
139                }
140            }
141        }
142    }
143    false
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149
150    #[test]
151    fn test_parse_postman_environment() {
152        let env_json = r#"{
153            "id": "env-123",
154            "name": "Development Environment",
155            "values": [
156                {
157                    "key": "base_url",
158                    "value": "https://api.dev.example.com",
159                    "description": "API base URL",
160                    "enabled": true
161                },
162                {
163                    "key": "api_key",
164                    "value": "dev-key-123",
165                    "enabled": true
166                },
167                {
168                    "key": "disabled_var",
169                    "value": "should_not_import",
170                    "enabled": false
171                }
172            ]
173        }"#;
174
175        let result = import_postman_environment(env_json).unwrap();
176
177        assert_eq!(result.name, "Development Environment");
178        assert_eq!(result.total_count, 3);
179        assert_eq!(result.enabled_count, 2);
180        assert_eq!(result.variables.len(), 2);
181
182        // Check base_url variable
183        let base_url_var = result.variables.get("base_url").unwrap();
184        assert_eq!(base_url_var.value, "https://api.dev.example.com");
185        assert_eq!(base_url_var.description.as_ref().unwrap(), "API base URL");
186        assert!(base_url_var.enabled);
187
188        // Check api_key variable
189        let api_key_var = result.variables.get("api_key").unwrap();
190        assert_eq!(api_key_var.value, "dev-key-123");
191        assert!(api_key_var.enabled);
192    }
193
194    #[test]
195    fn test_detect_postman_environment() {
196        let env_json = r#"{
197            "id": "env-123",
198            "name": "Test Environment",
199            "values": [
200                {
201                    "key": "test_var",
202                    "value": "test_value",
203                    "enabled": true
204                }
205            ]
206        }"#;
207
208        assert!(is_postman_environment_json(env_json));
209
210        let not_env_json = r#"{
211            "info": {
212                "name": "Test Collection"
213            },
214            "item": []
215        }"#;
216
217        assert!(!is_postman_environment_json(not_env_json));
218    }
219
220    #[test]
221    fn test_parse_minimal_environment() {
222        let minimal_env_json = r#"{
223            "values": [
224                {
225                    "key": "minimal_var",
226                    "value": "minimal_value",
227                    "enabled": true
228                }
229            ]
230        }"#;
231
232        let result = import_postman_environment(minimal_env_json).unwrap();
233
234        assert_eq!(result.name, "Unnamed Environment");
235        assert_eq!(result.total_count, 1);
236        assert_eq!(result.enabled_count, 1);
237        assert_eq!(result.variables.len(), 1);
238    }
239}