schemadoc_diff/checker/
updated_schema_type_check.rs

1use std::cell::RefCell;
2
3use crate::core::{DiffResult, MapDiff, VecDiff};
4use crate::path_pointer::PathPointer;
5
6use crate::checker::{ValidationIssue, ValidationIssuer};
7use crate::schema_diff::{
8    MayBeRefDiff, MediaTypeDiff, OperationDiff, ParameterDiff,
9    RequestBodyDiff, ResponseDiff, SchemaDiff,
10};
11use crate::visitor::DiffVisitor;
12
13pub struct UpdatedSchemaTypeCheck {
14    pointers: RefCell<Vec<PathPointer>>,
15}
16
17impl<'s> DiffVisitor<'s> for UpdatedSchemaTypeCheck {
18    fn visit_operation(
19        &self,
20        pointer: &PathPointer,
21        _: &str,
22        _: &'s DiffResult<OperationDiff>,
23    ) -> bool {
24        pointer.is_updated()
25    }
26
27    fn visit_request_body(
28        &self,
29        pointer: &PathPointer,
30        _: &'s DiffResult<RequestBodyDiff>,
31    ) -> bool {
32        pointer.is_updated()
33    }
34
35    fn visit_responses(
36        &self,
37        pointer: &PathPointer,
38        _: &'s DiffResult<MapDiff<MayBeRefDiff<ResponseDiff>>>,
39    ) -> bool {
40        pointer.is_updated()
41    }
42
43    fn visit_media_types(
44        &self,
45        pointer: &PathPointer,
46        _: &'s DiffResult<MapDiff<MediaTypeDiff>>,
47    ) -> bool {
48        pointer.is_updated()
49    }
50
51    fn visit_media_type(
52        &self,
53        pointer: &PathPointer,
54        _: &'s DiffResult<MediaTypeDiff>,
55    ) -> bool {
56        pointer.is_updated()
57    }
58
59    fn visit_parameters(
60        &self,
61        pointer: &PathPointer,
62        _: &'s DiffResult<VecDiff<MayBeRefDiff<ParameterDiff>>>,
63    ) -> bool {
64        pointer.is_updated()
65    }
66
67    fn visit_parameter(
68        &self,
69        pointer: &PathPointer,
70        _: &'s DiffResult<ParameterDiff>,
71    ) -> bool {
72        pointer.is_updated()
73    }
74
75    fn visit_schema(
76        &self,
77        pointer: &PathPointer,
78        schema_diff_result: &'s DiffResult<SchemaDiff>,
79    ) -> bool {
80        if !pointer.is_updated() {
81            return false;
82        }
83
84        let Some(schema) = schema_diff_result.get() else {
85            return false;
86        };
87
88        if schema.r#type.is_updated() {
89            self.pointers.borrow_mut().push(pointer.clone())
90        }
91
92        true
93    }
94}
95
96impl Default for UpdatedSchemaTypeCheck {
97    fn default() -> Self {
98        Self {
99            pointers: RefCell::new(vec![]),
100        }
101    }
102}
103
104impl<'s> ValidationIssuer<'s> for UpdatedSchemaTypeCheck {
105    fn id(&self) -> &'static str {
106        "updated-schema-type"
107    }
108
109    fn visitor(&self) -> &dyn DiffVisitor<'s> {
110        self
111    }
112
113    fn issues(&self) -> Option<Vec<ValidationIssue>> {
114        let pointers = std::mem::take(&mut *self.pointers.borrow_mut());
115
116        let issues = pointers
117            .into_iter()
118            .map(|path| ValidationIssue::new(path, self.id(), true))
119            .collect::<Vec<ValidationIssue>>();
120
121        Some(issues)
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use crate::checker::updated_schema_type_check::UpdatedSchemaTypeCheck;
128    use crate::checker::ValidationIssuer;
129    use crate::get_schema_diff;
130    use crate::schema::HttpSchema;
131    use crate::schemas::openapi303::schema::OpenApi303;
132
133    #[test]
134    fn test_updated_schema_type_check() {
135        let src_schema: HttpSchema = serde_json::from_str::<OpenApi303>(
136            include_str!("../../data/checks/updated-schema-type/schema.json"),
137        )
138        .unwrap()
139        .into();
140
141        let tgt_schema: HttpSchema =
142            serde_json::from_str::<OpenApi303>(include_str!(
143                "../../data/checks/updated-schema-type/schema-altered.json"
144            ))
145            .unwrap()
146            .into();
147
148        let diff = get_schema_diff(src_schema, tgt_schema);
149
150        let checker = UpdatedSchemaTypeCheck::default();
151        crate::visitor::dispatch_visitor(diff.get().unwrap(), &checker);
152        let issues = checker.issues().unwrap();
153
154        assert_eq!(issues.len(), 2);
155        assert_eq!(
156            issues.get(0).unwrap().path.get_path(),
157            "paths//test/post/requestBody/content/application/json/schema",
158        );
159        assert_eq!(
160            issues.get(1).unwrap().path.get_path(),
161            "paths//test2/post/responses/404/content/application/json/schema/properties/prop2",
162        );
163    }
164}