schemadoc_diff/checker/
updated_schema_type_check.rs1use 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}