• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geo-engine / geoengine / 13809415963

12 Mar 2025 10:42AM UTC coverage: 90.026% (-0.05%) from 90.076%
13809415963

Pull #1013

github

web-flow
Merge b51e2554c into c96026921
Pull Request #1013: Update-utoipa

787 of 935 new or added lines in 41 files covered. (84.17%)

28 existing lines in 10 files now uncovered.

125995 of 139954 relevant lines covered (90.03%)

57510.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.58
/services/src/util/openapi_visitors.rs
1
use std::collections::{HashMap, HashSet};
2

3
use utoipa::openapi::{OpenApi, RefOr, Schema};
4

5
use super::openapi_visitor::{visit_api, OpenapiVisitor};
6

7
struct CanResolveVisitor {
8
    pub unknown_ref: Option<String>,
9
}
10

11
impl OpenapiVisitor for CanResolveVisitor {
12
    fn resolve_failed(&mut self, ref_location: &str) {
8✔
13
        self.unknown_ref = Some(ref_location.to_owned());
8✔
14
    }
8✔
15
}
16

17
/// Loops through all registered HTTP handlers and ensures that the referenced schemas
18
/// (inside of request bodies, parameters or responses) exist and can be resolved.
19
///
20
/// # Panics
21
///
22
/// Panics if a referenced schema cannot be resolved.
23
///
24
pub fn can_resolve_api(api: &OpenApi) {
5✔
25
    let mut visitor = CanResolveVisitor { unknown_ref: None };
5✔
26
    visit_api(api, &mut visitor);
5✔
27

28
    if let Some(unknown_ref) = visitor.unknown_ref {
5✔
29
        panic!("Cannot resolve reference {unknown_ref}");
3✔
30
    }
2✔
31
}
2✔
32

33
struct SchemaUseCounter {
34
    parents: HashMap<String, HashSet<String>>,
35
}
36

37
impl SchemaUseCounter {
38
    fn get_schema_use_counts(self) -> HashMap<String, usize> {
1✔
39
        self.parents
1✔
40
            .into_iter()
1✔
41
            .map(|(key, parent_set)| (key, parent_set.len()))
3✔
42
            .collect()
1✔
43
    }
1✔
44
}
45

46
impl OpenapiVisitor for SchemaUseCounter {
47
    fn visit_schema_component(
4✔
48
        &mut self,
4✔
49
        name: &str,
4✔
50
        _schema: &RefOr<Schema>,
4✔
51
        source_location: &str,
4✔
52
    ) {
4✔
53
        let parent_set = self.parents.entry(name.to_owned()).or_default();
4✔
54
        parent_set.insert(source_location.to_owned());
4✔
55
    }
4✔
56
}
57

UNCOV
58
pub fn get_schema_use_counts(api: &OpenApi) -> HashMap<String, usize> {
×
UNCOV
59
    let mut visitor = SchemaUseCounter {
×
UNCOV
60
        parents: HashMap::new(),
×
UNCOV
61
    };
×
UNCOV
62
    visit_api(api, &mut visitor);
×
UNCOV
63
    visitor.get_schema_use_counts()
×
UNCOV
64
}
×
65

66
#[cfg(test)]
67
mod tests {
68
    use geoengine_datatypes::hashmap;
69
    use utoipa::openapi::{
70
        path::{OperationBuilder, ParameterBuilder, PathItemBuilder},
71
        request_body::RequestBodyBuilder,
72
        AllOfBuilder, ArrayBuilder, Components, ComponentsBuilder, ContentBuilder, HttpMethod,
73
        Object, ObjectBuilder, OneOfBuilder, OpenApiBuilder, PathsBuilder, Ref, ResponseBuilder,
74
    };
75

76
    use crate::util::openapi_visitor::visit_schema;
77

78
    use super::*;
79

80
    fn try_resolve_schema(schema: &RefOr<Schema>, components: &Components) {
5✔
81
        let mut visitor = CanResolveVisitor { unknown_ref: None };
5✔
82
        visit_schema(schema, components, &mut visitor, "root");
5✔
83

84
        if let Some(unknown_ref) = visitor.unknown_ref {
5✔
85
            panic!("Cannot resolve reference {unknown_ref}");
5✔
86
        }
×
87
    }
×
88

89
    fn count_schema_uses(
1✔
90
        schema: &RefOr<Schema>,
1✔
91
        components: &Components,
1✔
92
    ) -> HashMap<String, usize> {
1✔
93
        let mut visitor = SchemaUseCounter {
1✔
94
            parents: HashMap::new(),
1✔
95
        };
1✔
96
        visit_schema(schema, components, &mut visitor, "root");
1✔
97
        visitor.get_schema_use_counts()
1✔
98
    }
1✔
99

100
    #[test]
101
    #[should_panic(expected = "MissingSchema")]
102
    fn detects_missing_array_ref() {
1✔
103
        try_resolve_schema(
1✔
104
            &RefOr::T(
1✔
105
                ArrayBuilder::new()
1✔
106
                    .items(Ref::from_schema_name("MissingSchema"))
1✔
107
                    .into(),
1✔
108
            ),
1✔
109
            &Components::new(),
1✔
110
        );
1✔
111
    }
1✔
112

113
    #[test]
114
    #[should_panic(expected = "MissingSchema")]
115
    fn detects_missing_object_ref() {
1✔
116
        try_resolve_schema(
1✔
117
            &RefOr::T(
1✔
118
                ObjectBuilder::new()
1✔
119
                    .property("Prop", Ref::from_schema_name("MissingSchema"))
1✔
120
                    .into(),
1✔
121
            ),
1✔
122
            &Components::new(),
1✔
123
        );
1✔
124
    }
1✔
125

126
    #[test]
127
    #[should_panic(expected = "MissingSchema")]
128
    fn detects_missing_oneof_ref() {
1✔
129
        try_resolve_schema(
1✔
130
            &RefOr::T(
1✔
131
                OneOfBuilder::new()
1✔
132
                    .item(Ref::from_schema_name("MissingSchema"))
1✔
133
                    .into(),
1✔
134
            ),
1✔
135
            &Components::new(),
1✔
136
        );
1✔
137
    }
1✔
138

139
    #[test]
140
    #[should_panic(expected = "MissingSchema")]
141
    fn detects_missing_allof_ref() {
1✔
142
        try_resolve_schema(
1✔
143
            &RefOr::T(
1✔
144
                AllOfBuilder::new()
1✔
145
                    .item(Ref::from_schema_name("MissingSchema"))
1✔
146
                    .into(),
1✔
147
            ),
1✔
148
            &Components::new(),
1✔
149
        );
1✔
150
    }
1✔
151

152
    #[test]
153
    #[should_panic(expected = "Inner")]
154
    fn detects_missing_nested_schema() {
1✔
155
        try_resolve_schema(
1✔
156
            &RefOr::Ref(Ref::from_schema_name("Outer")),
1✔
157
            &ComponentsBuilder::new()
1✔
158
                .schema("Outer", RefOr::Ref(Ref::from_schema_name("Inner")))
1✔
159
                .into(),
1✔
160
        );
1✔
161
    }
1✔
162

163
    #[test]
164
    fn counts_schema_uses() {
1✔
165
        let uses = count_schema_uses(
1✔
166
            &RefOr::Ref(Ref::from_schema_name("Component")),
1✔
167
            &ComponentsBuilder::new()
1✔
168
                .schema(
1✔
169
                    "Component",
1✔
170
                    OneOfBuilder::new()
1✔
171
                        .item(Ref::from_schema_name("A"))
1✔
172
                        .item(Ref::from_schema_name("B")),
1✔
173
                )
1✔
174
                .schema("A", Object::new())
1✔
175
                .schema("B", Ref::from_schema_name("A"))
1✔
176
                .into(),
1✔
177
        );
1✔
178
        assert_eq!(
1✔
179
            uses,
1✔
180
            hashmap! {
1✔
181
                "Component".to_owned() => 1usize,
1✔
182
                "A".to_owned() => 2usize,
1✔
183
                "B".to_owned() => 1usize
1✔
184
            }
1✔
185
        );
1✔
186
    }
1✔
187

188
    #[test]
189
    fn successfull_api_validation() {
1✔
190
        let api: OpenApi = OpenApiBuilder::new()
1✔
191
            .paths(
1✔
192
                PathsBuilder::new().path(
1✔
193
                    "asdf",
1✔
194
                    PathItemBuilder::new()
1✔
195
                        .operation(
1✔
196
                            HttpMethod::Post,
1✔
197
                            OperationBuilder::new()
1✔
198
                                .parameter(
1✔
199
                                    ParameterBuilder::new()
1✔
200
                                        .schema(Some(Ref::from_schema_name("Schema1"))),
1✔
201
                                )
1✔
202
                                .request_body(Some(
1✔
203
                                    RequestBodyBuilder::new()
1✔
204
                                        .content(
1✔
205
                                            "application/json",
1✔
206
                                            ContentBuilder::new()
1✔
207
                                                .schema(Some(Ref::from_schema_name("Schema2")))
1✔
208
                                                .into(),
1✔
209
                                        )
1✔
210
                                        .into(),
1✔
211
                                ))
1✔
212
                                .response(
1✔
213
                                    "200",
1✔
214
                                    ResponseBuilder::new().content(
1✔
215
                                        "application/json",
1✔
216
                                        ContentBuilder::new()
1✔
217
                                            .schema(Some(Ref::from_schema_name("Schema3")))
1✔
218
                                            .into(),
1✔
219
                                    ),
1✔
220
                                ),
1✔
221
                        )
1✔
222
                        .into(),
1✔
223
                ),
1✔
224
            )
1✔
225
            .components(Some(
1✔
226
                ComponentsBuilder::new()
1✔
227
                    .schemas_from_iter([
1✔
228
                        ("Schema1", Schema::default()),
1✔
229
                        ("Schema2", Schema::default()),
1✔
230
                        ("Schema3", Schema::default()),
1✔
231
                    ])
1✔
232
                    .into(),
1✔
233
            ))
1✔
234
            .into();
1✔
235
        can_resolve_api(&api);
1✔
236
    }
1✔
237

238
    #[test]
239
    #[should_panic(expected = "MissingSchema")]
240
    fn detect_unresolvable_request_body() {
1✔
241
        let api: OpenApi = OpenApiBuilder::new()
1✔
242
            .paths(
1✔
243
                PathsBuilder::new().path(
1✔
244
                    "asdf",
1✔
245
                    PathItemBuilder::new()
1✔
246
                        .operation(
1✔
247
                            HttpMethod::Post,
1✔
248
                            OperationBuilder::new().request_body(Some(
1✔
249
                                RequestBodyBuilder::new()
1✔
250
                                    .content(
1✔
251
                                        "application/json",
1✔
252
                                        ContentBuilder::new()
1✔
253
                                            .schema(Some(Ref::from_schema_name("MissingSchema")))
1✔
254
                                            .into(),
1✔
255
                                    )
1✔
256
                                    .into(),
1✔
257
                            )),
1✔
258
                        )
1✔
259
                        .into(),
1✔
260
                ),
1✔
261
            )
1✔
262
            .components(Some(
1✔
263
                ComponentsBuilder::new()
1✔
264
                    .schemas_from_iter([
1✔
265
                        ("Schema1", Schema::default()),
1✔
266
                        ("Schema2", Schema::default()),
1✔
267
                        ("Schema3", Schema::default()),
1✔
268
                    ])
1✔
269
                    .into(),
1✔
270
            ))
1✔
271
            .into();
1✔
272
        can_resolve_api(&api);
1✔
273
    }
1✔
274

275
    #[test]
276
    #[should_panic(expected = "MissingSchema")]
277
    fn detect_unresolvable_parameter() {
1✔
278
        let api: OpenApi = OpenApiBuilder::new()
1✔
279
            .paths(
1✔
280
                PathsBuilder::new().path(
1✔
281
                    "asdf",
1✔
282
                    PathItemBuilder::new()
1✔
283
                        .operation(
1✔
284
                            HttpMethod::Post,
1✔
285
                            OperationBuilder::new().parameter(
1✔
286
                                ParameterBuilder::new()
1✔
287
                                    .schema(Some(Ref::from_schema_name("MissingSchema"))),
1✔
288
                            ),
1✔
289
                        )
1✔
290
                        .into(),
1✔
291
                ),
1✔
292
            )
1✔
293
            .components(Some(
1✔
294
                ComponentsBuilder::new()
1✔
295
                    .schemas_from_iter([
1✔
296
                        ("Schema1", Schema::default()),
1✔
297
                        ("Schema2", Schema::default()),
1✔
298
                        ("Schema3", Schema::default()),
1✔
299
                    ])
1✔
300
                    .into(),
1✔
301
            ))
1✔
302
            .into();
1✔
303
        can_resolve_api(&api);
1✔
304
    }
1✔
305

306
    #[test]
307
    #[should_panic(expected = "MissingSchema")]
308
    fn detect_unresolvable_response() {
1✔
309
        let api: OpenApi = OpenApiBuilder::new()
1✔
310
            .paths(
1✔
311
                PathsBuilder::new().path(
1✔
312
                    "asdf",
1✔
313
                    PathItemBuilder::new()
1✔
314
                        .operation(
1✔
315
                            HttpMethod::Post,
1✔
316
                            OperationBuilder::new().response(
1✔
317
                                "200",
1✔
318
                                ResponseBuilder::new().content(
1✔
319
                                    "application/json",
1✔
320
                                    ContentBuilder::new()
1✔
321
                                        .schema(Some(Ref::from_schema_name("MissingSchema")))
1✔
322
                                        .into(),
1✔
323
                                ),
1✔
324
                            ),
1✔
325
                        )
1✔
326
                        .into(),
1✔
327
                ),
1✔
328
            )
1✔
329
            .components(Some(
1✔
330
                ComponentsBuilder::new()
1✔
331
                    .schemas_from_iter([
1✔
332
                        ("Schema1", Schema::default()),
1✔
333
                        ("Schema2", Schema::default()),
1✔
334
                        ("Schema3", Schema::default()),
1✔
335
                    ])
1✔
336
                    .into(),
1✔
337
            ))
1✔
338
            .into();
1✔
339
        can_resolve_api(&api);
1✔
340
    }
1✔
341
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc