1use crate::ogcapi::*;
2
3#[derive(Clone, Default)]
4pub struct OgcApiInventory {
5 pub landing_page_links: Vec<ApiLink>,
6 pub conformance_classes: Vec<String>,
7 pub collections: Vec<CoreCollection>,
8}
9
10#[derive(Default, Clone)]
12pub struct OpenApiDoc(serde_yaml::Value);
13
14impl OpenApiDoc {
15 pub fn new() -> Self {
16 Self::from_yaml("{}", "")
17 }
18 pub fn from_yaml(yaml: &str, _prefix: &str) -> Self {
19 OpenApiDoc(serde_yaml::from_str(yaml).unwrap())
20 }
21 pub fn is_empty(&self) -> bool {
22 self.0 == Self::new().0
23 }
24 pub fn extend(&mut self, yaml: &str, _prefix: &str) {
26 let rhs_yaml = serde_yaml::from_str(yaml).unwrap();
27 merge_level(&mut self.0, &rhs_yaml, "paths");
28 if let Some(rhs_components) = rhs_yaml.get("components") {
29 if let Some(components) = self.0.get_mut("components") {
30 for (key, _val) in rhs_components.as_mapping().unwrap().iter() {
32 merge_level(components, rhs_components, key.as_str().unwrap());
33 }
34 } else {
35 self.0
36 .as_mapping_mut()
37 .unwrap()
38 .insert("components".into(), rhs_components.clone());
39 }
40 }
41 }
42 pub fn set_server_url(&mut self, url: &str) {
44 if let Some(servers) = self.0.get_mut("servers") {
45 if let Some(server) = servers.get_mut(0) {
46 if let Some(server) = server.as_mapping_mut() {
47 server[&"url".to_string().into()] = url.to_string().into();
48 }
49 }
50 }
51 }
52 pub fn as_yaml(&self, public_server_url: &str) -> String {
53 let mut doc = self.clone();
54 doc.set_server_url(public_server_url);
55 serde_yaml::to_string(&doc.0).unwrap()
56 }
57 pub fn as_json(&self, public_server_url: &str) -> serde_json::Value {
58 let mut doc = self.clone();
59 doc.set_server_url(public_server_url);
60 serde_yaml::from_value::<serde_json::Value>(doc.0).unwrap()
61 }
62}
63
64fn merge_level(yaml: &mut serde_yaml::Value, rhs_yaml: &serde_yaml::Value, key: &str) {
65 if let Some(rhs_elem) = rhs_yaml.get(key) {
66 if let Some(elem) = yaml.get_mut(key) {
67 elem.as_mapping_mut()
68 .unwrap()
69 .extend(rhs_elem.as_mapping().unwrap().clone());
70 } else {
71 yaml.as_mapping_mut()
72 .unwrap()
73 .insert(key.into(), rhs_elem.clone());
74 }
75 }
76}
77
78#[cfg(test)]
79mod test {
80 use super::*;
81
82 const YAML_BASE: &str = r##"---
83openapi: 3.0.2
84info:
85 title: BBOX OGC API
86servers:
87 - url: "http://bbox:8080/"
88 description: Production server
89paths:
90 /conformance:
91 get:
92 operationId: getConformance
93 responses:
94 "333":
95 $ref: "#/components/schemas/confClasses"
96components:
97 schemas:
98 confClasses:
99 type: object
100 required:
101 - conformsTo
102 properties:
103 conformsTo:
104 type: array
105 items:
106 type: string
107"##;
108
109 #[test]
110 fn yaml_empty() {
111 let doc = OpenApiDoc::new();
112 assert!(doc.is_empty());
113 }
114
115 #[test]
116 fn yaml_extend_headers() {
117 let mut doc = OpenApiDoc::from_yaml(YAML_BASE, "");
118 assert_eq!(doc.as_yaml("http://bbox:8080/"), YAML_BASE);
119
120 let yaml = r##"---
121openapi: 3.0.0
122info:
123 title: New Title
124"##;
125 doc.extend(yaml, "");
126 assert_eq!(doc.as_yaml("http://bbox:8080/"), YAML_BASE);
127 }
128
129 #[test]
130 fn yaml_add_path() {
131 let yaml = r#"---
132paths:
133 /newpath: ""
134"#;
135 let yamlout = r##"---
136openapi: 3.0.2
137info:
138 title: BBOX OGC API
139servers:
140 - url: "http://bbox:8080/"
141 description: Production server
142paths:
143 /conformance:
144 get:
145 operationId: getConformance
146 responses:
147 "333":
148 $ref: "#/components/schemas/confClasses"
149 /newpath: ""
150components:
151 schemas:
152 confClasses:
153 type: object
154 required:
155 - conformsTo
156 properties:
157 conformsTo:
158 type: array
159 items:
160 type: string
161"##;
162 let mut doc = OpenApiDoc::from_yaml(YAML_BASE, "");
163 doc.extend(yaml, "");
164 assert_eq!(doc.as_yaml("http://bbox:8080/"), yamlout);
165 }
166
167 #[test]
168 fn yaml_update_path() {
169 let yaml = r##"---
170paths:
171 /conformance:
172 get:
173 operationId: getConformance
174 responses:
175 "333":
176 $ref: "#/components/schemas/confClasses"
177"##;
178 let mut doc = OpenApiDoc::from_yaml(YAML_BASE, "");
179 doc.extend(yaml, "");
180 assert_eq!(doc.as_yaml("http://bbox:8080/"), YAML_BASE);
181 }
182
183 #[test]
184 fn yaml_change_path() {
185 let yaml = r##"---
186paths:
187 /conformance:
188 post:
189 operationId: postConformance
190 responses:
191 "333":
192 $ref: "#/components/schemas/confClasses"
193"##;
194 let yamlout = r##"---
195openapi: 3.0.2
196info:
197 title: BBOX OGC API
198servers:
199 - url: "http://bbox:8080/"
200 description: Production server
201paths:
202 /conformance:
203 post:
204 operationId: postConformance
205 responses:
206 "333":
207 $ref: "#/components/schemas/confClasses"
208components:
209 schemas:
210 confClasses:
211 type: object
212 required:
213 - conformsTo
214 properties:
215 conformsTo:
216 type: array
217 items:
218 type: string
219"##;
220 let mut doc = OpenApiDoc::from_yaml(YAML_BASE, "");
221 doc.extend(yaml, "");
222 assert_eq!(doc.as_yaml("http://bbox:8080/"), yamlout);
223 }
224
225 #[test]
226 fn yaml_add_component() {
227 let yaml = r##"---
228components:
229 schemas:
230 link:
231 type: object
232 confClasses:
233 type: object
234 required:
235 - conformsTo
236 properties:
237 conformsTo:
238 type: array
239 items:
240 type: string
241"##;
242 let yamlout = r##"---
243openapi: 3.0.2
244info:
245 title: BBOX OGC API
246servers:
247 - url: "http://bbox:8080/"
248 description: Production server
249paths:
250 /conformance:
251 get:
252 operationId: getConformance
253 responses:
254 "333":
255 $ref: "#/components/schemas/confClasses"
256components:
257 schemas:
258 confClasses:
259 type: object
260 required:
261 - conformsTo
262 properties:
263 conformsTo:
264 type: array
265 items:
266 type: string
267 link:
268 type: object
269"##;
270 let mut doc = OpenApiDoc::from_yaml(YAML_BASE, "");
271 doc.extend(yaml, "");
272 assert_eq!(doc.as_yaml("http://bbox:8080/"), yamlout);
273 }
274
275 #[test]
276 fn yaml_add_new_component() {
277 let yaml = r##"---
278components:
279 responses:
280 NotFound:
281 description: The requested resource does not exist.
282"##;
283 let yamlout = r##"---
284openapi: 3.0.2
285info:
286 title: BBOX OGC API
287servers:
288 - url: "http://bbox:8080/"
289 description: Production server
290paths:
291 /conformance:
292 get:
293 operationId: getConformance
294 responses:
295 "333":
296 $ref: "#/components/schemas/confClasses"
297components:
298 schemas:
299 confClasses:
300 type: object
301 required:
302 - conformsTo
303 properties:
304 conformsTo:
305 type: array
306 items:
307 type: string
308 responses:
309 NotFound:
310 description: The requested resource does not exist.
311"##;
312 let mut doc = OpenApiDoc::from_yaml(YAML_BASE, "");
313 doc.extend(yaml, "");
314 assert_eq!(doc.as_yaml("http://bbox:8080/"), yamlout);
315 }
316}