schemadoc_diff/checker/
added_required_request_body_check.rs

1use std::cell::RefCell;
2
3use crate::checker::{ValidationIssue, ValidationIssuer};
4use crate::core::DiffResult;
5use crate::path_pointer::PathPointer;
6use crate::schema_diff::{OperationDiff, RequestBodyDiff};
7
8use crate::visitor::DiffVisitor;
9
10pub struct AddedRequiredRequestBodyCheck {
11    pointers: RefCell<Vec<PathPointer>>,
12}
13
14impl<'s> DiffVisitor<'s> for AddedRequiredRequestBodyCheck {
15    fn visit_operation(
16        &self,
17        pointer: &PathPointer,
18        _: &str,
19        _: &'s DiffResult<OperationDiff>,
20    ) -> bool {
21        // Only check already existed operations
22        pointer.is_updated()
23    }
24
25    fn visit_request_body(
26        &self,
27        pointer: &PathPointer,
28        request_body_diff_result: &'s DiffResult<RequestBodyDiff>,
29    ) -> bool {
30        if !pointer.is_upserted() {
31            return false;
32        }
33
34        let Some(request_body_diff) = request_body_diff_result.get() else {
35            return false;
36        };
37
38        if !request_body_diff.required.is_upserted() {
39            return false;
40        }
41
42        if let Some(required) = request_body_diff.required.get() {
43            if *required {
44                self.pointers.borrow_mut().push(pointer.clone())
45            }
46        }
47
48        false
49    }
50}
51
52impl Default for AddedRequiredRequestBodyCheck {
53    fn default() -> Self {
54        AddedRequiredRequestBodyCheck {
55            pointers: RefCell::new(vec![]),
56        }
57    }
58}
59
60impl<'s> ValidationIssuer<'s> for AddedRequiredRequestBodyCheck {
61    fn id(&self) -> &'static str {
62        "added-required-request-body"
63    }
64
65    fn visitor(&self) -> &dyn DiffVisitor<'s> {
66        self
67    }
68
69    fn issues(&self) -> Option<Vec<ValidationIssue>> {
70        let pointers = std::mem::take(&mut *self.pointers.borrow_mut());
71
72        let issues = pointers
73            .into_iter()
74            .map(|path| ValidationIssue::new(path, self.id(), true))
75            .collect::<Vec<ValidationIssue>>();
76
77        Some(issues)
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use crate::checker::added_required_request_body_check::AddedRequiredRequestBodyCheck;
84    use crate::checker::ValidationIssuer;
85    use crate::get_schema_diff;
86    use crate::schema::HttpSchema;
87    use crate::schemas::openapi303::schema::OpenApi303;
88
89    #[test]
90    fn test_added_required_request_body_check() {
91        let src_schema: HttpSchema = serde_json::from_str::<OpenApi303>(include_str!(
92            "../../data/checks/added-required-request-body/schema-with-required-body.json"
93        ))
94        .unwrap()
95        .into();
96
97        let tgt_schema: HttpSchema = serde_json::from_str::<OpenApi303>(include_str!(
98            "../../data/checks/added-required-request-body/schema-with-required-body-altered.json"
99        ))
100        .unwrap()
101        .into();
102
103        let diff = get_schema_diff(src_schema, tgt_schema);
104
105        let checker = AddedRequiredRequestBodyCheck::default();
106        crate::visitor::dispatch_visitor(diff.get().unwrap(), &checker);
107        let issues = checker.issues().unwrap();
108
109        assert_eq!(issues.len(), 2);
110        assert_eq!(
111            issues.get(0).unwrap().path.get_path(),
112            "paths//test/post/requestBody",
113        );
114        assert_eq!(
115            issues.get(1).unwrap().path.get_path(),
116            "paths//test/patch/requestBody",
117        );
118    }
119}