schemadoc_diff/checker/
added_required_body_property_check.rs1use std::cell::RefCell;
2
3use crate::checker::{ValidationIssue, ValidationIssuer};
4use crate::core::{DiffResult, MapDiff};
5use crate::path_pointer::PathPointer;
6use crate::schema_diff::{
7 MediaTypeDiff, OperationDiff, RequestBodyDiff, SchemaDiff,
8};
9
10use crate::visitor::DiffVisitor;
11
12pub struct AddedRequiredBodyPropertyCheck {
13 pointers: RefCell<Vec<PathPointer>>,
14}
15
16impl<'s> DiffVisitor<'s> for AddedRequiredBodyPropertyCheck {
17 fn visit_operation(
18 &self,
19 pointer: &PathPointer,
20 _: &str,
21 _: &'s DiffResult<OperationDiff>,
22 ) -> bool {
23 pointer.is_updated()
24 }
25
26 fn visit_request_body(
27 &self,
28 pointer: &PathPointer,
29 request_body_diff_result: &'s DiffResult<RequestBodyDiff>,
30 ) -> bool {
31 if !pointer.is_updated() {
32 return false;
33 }
34 if let Some(request_body_diff) = request_body_diff_result.get() {
36 request_body_diff
37 .required
38 .get()
39 .map(|v| *v)
40 .unwrap_or(false)
41 } else {
42 false
43 }
44 }
45
46 fn visit_media_types(
47 &self,
48 pointer: &PathPointer,
49 _: &'s DiffResult<MapDiff<MediaTypeDiff>>,
50 ) -> bool {
51 pointer.is_updated()
52 }
53
54 fn visit_media_type(
55 &self,
56 pointer: &PathPointer,
57 _: &'s DiffResult<MediaTypeDiff>,
58 ) -> bool {
59 pointer.is_updated()
60 }
61
62 fn visit_schema(
63 &self,
64 pointer: &PathPointer,
65 _schema_diff_result: &'s DiffResult<SchemaDiff>,
66 ) -> bool {
67 if pointer.is_added() {
68 self.pointers.borrow_mut().push(pointer.clone());
69 return false;
70 }
71
72 pointer.is_updated()
73 }
74}
75
76impl Default for AddedRequiredBodyPropertyCheck {
77 fn default() -> Self {
78 AddedRequiredBodyPropertyCheck {
79 pointers: RefCell::new(vec![]),
80 }
81 }
82}
83
84impl<'s> ValidationIssuer<'s> for AddedRequiredBodyPropertyCheck {
85 fn id(&self) -> &'static str {
86 "added-required-body-property"
87 }
88
89 fn visitor(&self) -> &dyn DiffVisitor<'s> {
90 self
91 }
92
93 fn issues(&self) -> Option<Vec<ValidationIssue>> {
94 let pointers = std::mem::take(&mut *self.pointers.borrow_mut());
95
96 let issues = pointers
97 .into_iter()
98 .map(|path| ValidationIssue::new(path, self.id(), true))
99 .collect::<Vec<ValidationIssue>>();
100
101 Some(issues)
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use crate::checker::added_required_body_property_check::AddedRequiredBodyPropertyCheck;
108 use crate::checker::ValidationIssuer;
109 use crate::get_schema_diff;
110 use crate::schema::HttpSchema;
111 use crate::schemas::openapi303::schema::OpenApi303;
112
113 #[test]
114 fn test_added_required_property_check() {
115 let src_schema: HttpSchema = serde_json::from_str::<OpenApi303>(include_str!(
116 "../../data/checks/added-required-property/schema-with-required-body.json"
117 ))
118 .unwrap()
119 .into();
120
121 let tgt_schema: HttpSchema = serde_json::from_str::<OpenApi303>(include_str!(
122 "../../data/checks/added-required-property/schema-with-required-body-altered.json"
123 ))
124 .unwrap()
125 .into();
126
127 let diff = get_schema_diff(src_schema, tgt_schema);
128
129 let checker = AddedRequiredBodyPropertyCheck::default();
130 crate::visitor::dispatch_visitor(diff.get().unwrap(), &checker);
131 let issues = checker.issues().unwrap();
132
133 assert_eq!(issues.len(), 2);
134 assert_eq!(
136 issues.get(0).unwrap().path.get_path(),
137 "paths//test/put/requestBody/content/application/json/schema/properties/field1/allOf/0",
138 );
139 assert_eq!(
141 issues.get(1).unwrap().path.get_path(),
142 "paths//test/put/requestBody/content/application/json/schema/properties/field2",
143 );
144 }
145
146 #[test]
147 fn test_added_not_required_property_check() {
148 let src_schema: HttpSchema = serde_json::from_str::<OpenApi303>(include_str!(
149 "../../data/checks/added-required-property/schema-with-required-body.json"
150 ))
151 .unwrap()
152 .into();
153
154 let get_tgt_schema = || {
155 let mut schema: HttpSchema = serde_json::from_str::<OpenApi303>(include_str!(
156 "../../data/checks/added-required-property/schema-with-required-body-altered.json"
157 ))
158 .unwrap()
159 .into();
160
161 schema
162 .paths
163 .as_mut()?
164 .get_mut("/test")
165 .as_mut()?
166 .value_mut()?
167 .put
168 .as_mut()?
169 .request_body
170 .as_mut()?
171 .value_mut()?
172 .required = Some(false);
173
174 Some(schema)
175 };
176
177 let tgt_schema: HttpSchema = get_tgt_schema().unwrap();
178
179 let diff = get_schema_diff(src_schema, tgt_schema);
180
181 let checker = AddedRequiredBodyPropertyCheck::default();
182 crate::visitor::dispatch_visitor(diff.get().unwrap(), &checker);
183 let issues = checker.issues().unwrap();
184
185 assert!(issues.is_empty());
186 }
187}