openapi_utils/
dereferer.rs

1use crate::error::DerefError;
2use crate::reference::ReferenceOrExt;
3use alloc::boxed::Box;
4use alloc::string::String;
5use alloc::string::ToString;
6use alloc::vec::Vec;
7use indexmap::IndexMap;
8use openapiv3::*;
9
10/// Extends an openapi spec with a method to dereference all its contents
11pub trait SpecExt {
12    /// Dereferences all the $ref refences in the OpenAPI description.
13    /// Consumes the Spec object and creates a new one with all references resolved
14    /// Note that because the inner structures are using `ReferenceOr`
15    /// you will still need to use the methods from the `ReferenceOr` extension
16    /// to access the data. But these methods will always succeed as all
17    /// references have been resolved ahead of time.
18    ///
19    /// # Panics
20    /// This method will panic if the referenced item is not present in the OpenAPI description.
21    /// This method will panic AdditionalProperties
22    ///
23    /// # Example
24    ///
25    /// ```
26    ///   let data = std::fs::read_to_string(filename).expect("OpenAPI file could not be read.");
27    ///   let deser = serde_yaml::from_str(&data).expect("Could not deserialize file as OpenAPI v3.0");
28    ///   let spec = spec::read(&deser).deref_all();
29    ///   let paths = spec.paths;
30    /// ```
31    fn deref_all(self) -> OpenAPI;
32}
33
34impl SpecExt for OpenAPI {
35    /// Dereferences all the internal references in a document by copying
36    /// the items in the place of the references.
37    fn deref_all(mut self) -> OpenAPI {
38        if let Some(comp) = self.components.as_ref() {
39            for (_, path_item) in &mut self.paths.paths {
40                deref_everything_in_path(path_item, comp);
41            }
42        }
43        //    println!("{:?}", spec);
44        self
45    }
46}
47
48fn deref_everything_in_path(path_item: &mut ReferenceOr<PathItem>, components: &Components) {
49    let p_item = path_item.to_item_mut(); //TODO: no Deref possible? Where in components?
50    set_deref_all_params(&mut p_item.parameters, components);
51
52    let p_item2 = p_item.clone(); // hack as things are used, etc.
53
54    for operation in operation_list(p_item) {
55        // inline params
56        set_deref_all_params(&mut operation.parameters, components);
57        // Move from path level params to each operation so it is easier later
58        for param in &p_item2.parameters {
59            operation.parameters.push(param.clone());
60        }
61
62        // inline request body
63        if operation.request_body.is_some() {
64            let req_body = operation.request_body.as_mut().unwrap();
65            set_deref(req_body, &components.request_bodies, &mut Vec::new());
66            let body: &mut RequestBody = req_body.to_item_mut();
67            for (_, media) in &mut body.content {
68                if media.schema.is_none() {
69                    continue;
70                }
71                let schema = media.schema.as_mut().unwrap();
72                let mut referred = Vec::new();
73                set_deref(schema, &components.schemas, &mut referred);
74                set_defer_schema_contents(schema.to_item_mut(), components, 10, &mut referred);
75            }
76        }
77
78        // inline responses
79        for (_status_code, response) in &mut operation.responses.responses {
80            set_deref(response, &components.responses, &mut Vec::new());
81            for (_name, header) in &mut response.to_item_mut().headers {
82                set_deref(header, &components.headers, &mut Vec::new());
83            }
84            for (_, media) in &mut response.to_item_mut().content {
85                if media.schema.is_none() {
86                    continue;
87                }
88                let schema = media.schema.as_mut().unwrap();
89                let mut referred = Vec::new();
90                set_deref(schema, &components.schemas, &mut referred);
91                set_defer_schema_contents(schema.to_item_mut(), components, 10, &mut referred);
92            }
93        }
94    }
95}
96
97fn set_defer_schema_contents(
98    schema: &mut Schema,
99    components: &Components,
100    recursion: i32,
101    referred: &mut Vec<String>,
102) {
103    if recursion == 0 {
104        return;
105    }
106    match &mut schema.schema_kind {
107        SchemaKind::Type(schema_type) => match schema_type {
108            Type::Object(object) => {
109                for (_name, property) in &mut object.properties {
110                    set_deref_box(property, &components.schemas, referred);
111                    set_defer_schema_contents(
112                        property.to_item_mut(),
113                        components,
114                        recursion - 1,
115                        referred,
116                    );
117                }
118            }
119            Type::Array(array) => match &mut array.items {
120                None => {}
121                Some(items) => {
122                    set_deref_box(items, &components.schemas, referred);
123                    set_defer_schema_contents(
124                        items.to_item_mut(),
125                        components,
126                        recursion - 1,
127                        referred,
128                    );
129                }
130            },
131            _ => {}
132        },
133
134        SchemaKind::OneOf { ref mut one_of } => {
135            for sch in &mut one_of.iter_mut() {
136                set_deref(sch, &components.schemas, referred);
137                set_defer_schema_contents(sch.to_item_mut(), components, recursion - 1, referred);
138            }
139        }
140        SchemaKind::AnyOf { ref mut any_of } => {
141            for sch in &mut any_of.iter_mut() {
142                set_deref(sch, &components.schemas, referred);
143                set_defer_schema_contents(sch.to_item_mut(), components, recursion - 1, referred);
144            }
145        }
146        SchemaKind::AllOf { ref mut all_of } => {
147            for sch in &mut all_of.iter_mut() {
148                set_deref(sch, &components.schemas, referred);
149                set_defer_schema_contents(sch.to_item_mut(), components, recursion - 1, referred);
150            }
151        }
152        SchemaKind::Not { ref mut not } => {
153            let sch = not;
154            set_deref(sch, &components.schemas, referred);
155            set_defer_schema_contents(sch.to_item_mut(), components, recursion - 1, referred);
156        }
157        SchemaKind::Any(schema) => {
158            for (_name, property) in &mut schema.properties {
159                set_deref_box(property, &components.schemas, referred);
160                set_defer_schema_contents(property.to_item_mut(), components, recursion - 1, referred);
161            }
162            if schema.items.is_some() {
163                let the_items = schema.items.as_mut().unwrap();
164                set_deref_box(the_items, &components.schemas, referred);
165                set_defer_schema_contents(the_items.to_item_mut(), components, recursion - 1, referred);
166            }
167            for sch in &mut schema.one_of.iter_mut() {
168                set_deref(sch, &components.schemas, referred);
169                set_defer_schema_contents(sch.to_item_mut(), components, recursion - 1, referred);
170            }
171            for sch in &mut schema.any_of.iter_mut() {
172                set_deref(sch, &components.schemas, referred);
173                set_defer_schema_contents(sch.to_item_mut(), components, recursion - 1, referred);
174            }
175            for sch in &mut schema.all_of.iter_mut() {
176                set_deref(sch, &components.schemas, referred);
177                set_defer_schema_contents(sch.to_item_mut(), components, recursion - 1, referred);
178            }
179        }
180    }
181}
182
183fn set_deref_all_params(parameters: &mut [ReferenceOr<Parameter>], components: &Components) {
184    for parameter in parameters.iter_mut() {
185        set_deref(parameter, &components.parameters, &mut Vec::new());
186    }
187}
188
189fn set_deref<T>(
190    item: &mut ReferenceOr<T>,
191    component_type: &IndexMap<String, ReferenceOr<T>>,
192    referred: &mut Vec<String>,
193) where
194    T: Clone,
195{
196    match item {
197        ReferenceOr::Reference { reference } => {
198            let p = find_reference(reference, component_type, referred)
199                .expect("No Reference found!");
200            *item = ReferenceOr::Item(p);
201        }
202        ReferenceOr::Item(_) => {}
203    }
204}
205
206fn set_deref_box<T>(
207    item: &mut ReferenceOr<Box<T>>,
208    component_type: &IndexMap<String, ReferenceOr<T>>,
209    referred: &mut Vec<String>,
210) where
211    T: Clone,
212{
213    match item {
214        ReferenceOr::Reference { reference } => {
215            let p = find_reference(reference, component_type, referred)
216                .expect("No Reference found!");
217            *item = ReferenceOr::Item(Box::new(p));
218        }
219        ReferenceOr::Item(_) => {}
220    }
221}
222
223fn find_reference<T>(
224    reference: &str,
225    component_type: &IndexMap<String, ReferenceOr<T>>,
226    referred: &mut Vec<String>,
227) -> Result<T, DerefError>
228where
229    T: Clone,
230{
231    let reference_name: &str = reference.rsplit('/').next().unwrap();
232
233    let ref_item = component_type
234        .get(reference_name)
235        .ok_or(DerefError::ReferenceError {
236            name: reference_name.to_string(),
237        })?;
238
239    match ref_item {
240        ReferenceOr::Reference { reference } => {
241            if referred.iter().any(|v| v == reference_name) {
242                unimplemented!("Circular references are not supported")
243            } else {
244                referred.push(reference_name.to_string());
245                let p = find_reference(reference, component_type, referred)
246                    .expect("No Reference found!");
247                Ok(p)
248            }
249        }
250        ReferenceOr::Item(item) => Ok(item.clone()),
251    }
252}
253
254fn operation_list<'a>(item: &'a mut PathItem) -> Vec<&'a mut Operation> {
255    let mut result = Vec::new();
256    let mut pusher = |operation: &'a mut Option<Operation>| {
257        if operation.is_some() {
258            result.push(operation.as_mut().unwrap());
259        }
260    };
261    pusher(&mut item.delete);
262    pusher(&mut item.get);
263    pusher(&mut item.head);
264    pusher(&mut item.options);
265    pusher(&mut item.patch);
266    pusher(&mut item.post);
267    pusher(&mut item.put);
268    pusher(&mut item.trace);
269    result
270}