mycelium_openapi/dtos/
operation.rs

1use crate::{
2    dtos::{content::Content, parameter::Parameter},
3    entities::{DepthTracker, ReferenceResolver},
4};
5
6use mycelium_base::utils::errors::{execution_err, MappedErrors};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use utoipa::ToSchema;
10
11#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq, ToSchema)]
12#[serde(rename_all = "camelCase")]
13pub struct Operation {
14    #[serde(default)]
15    pub operation_id: Option<String>,
16
17    #[serde(default)]
18    pub tags: Vec<String>,
19
20    #[serde(default, skip_serializing_if = "Option::is_none")]
21    pub summary: Option<String>,
22
23    #[serde(default, skip_serializing_if = "Option::is_none")]
24    pub description: Option<String>,
25
26    #[serde(default, skip_serializing_if = "Option::is_none")]
27    pub parameters: Option<Vec<Parameter>>,
28
29    #[serde(default, skip_serializing_if = "Option::is_none")]
30    pub request_body: Option<Content>,
31
32    #[serde(default, skip_serializing_if = "Option::is_none")]
33    pub responses: Option<HashMap<String, Content>>,
34
35    #[serde(default, skip_serializing_if = "Option::is_none")]
36    pub deprecated: Option<bool>,
37
38    #[serde(default, skip_serializing_if = "Option::is_none")]
39    pub security: Option<serde_json::Value>,
40}
41
42impl ReferenceResolver for Operation {
43    fn resolve_ref(
44        &self,
45        components: &serde_json::Value,
46        depth_tracker: &mut DepthTracker,
47    ) -> Result<serde_json::Value, MappedErrors> {
48        if depth_tracker.should_stop() {
49            return Ok(depth_tracker.empty_value());
50        }
51
52        depth_tracker.increment();
53
54        let mut value = serde_json::to_value(self).map_err(|e| {
55            execution_err(format!("Failed to resolve operation: {e}"))
56        })?;
57
58        //
59        // Resolve the parameters
60        //
61        if let Some(parameters) = self.parameters.as_ref() {
62            value["parameters"] = if let false = parameters.is_empty() {
63                parameters
64                    .iter()
65                    .filter_map(|parameter| {
66                        //
67                        // Clone the depth tracker to guard against parallel
68                        // increment
69                        //
70                        let mut tracker_clone = depth_tracker.clone();
71
72                        //
73                        // Resolve the parameter
74                        //
75                        parameter
76                            .resolve_ref(components, &mut tracker_clone)
77                            .map_err(|e| {
78                                execution_err(format!(
79                                    "Failed to resolve parameter: {e}"
80                                ))
81                            })
82                            .ok()
83                    })
84                    .collect::<Vec<serde_json::Value>>()
85                    .into()
86            } else {
87                depth_tracker.empty_value()
88            };
89        }
90
91        //
92        // Resolve the request body
93        //
94        if let Some(request_body) = self.request_body.as_ref() {
95            value["requestBody"] =
96                request_body.resolve_ref(components, depth_tracker)?;
97        }
98
99        //
100        // Resolve the responses
101        //
102        if let Some(responses) = self.responses.as_ref() {
103            let mut responses_value = serde_json::Map::new();
104
105            for (key, response) in responses {
106                let resolved =
107                    response.resolve_ref(components, depth_tracker)?;
108
109                responses_value.insert(key.clone(), resolved);
110            }
111
112            value["responses"] = serde_json::Value::Object(responses_value);
113        }
114
115        Ok(value)
116    }
117}