backend/config/
api_doc.rs

1//! Contains code to generate `OpenApi` documentation and a `Swagger` endpoint using [`utoipa`] and [`utoipa_swagger_ui`].
2
3#![allow(clippy::needless_for_each)]
4
5use actix_web::web;
6use utoipa::{
7    openapi::security::{AuthorizationCode, Flow, OAuth2, Scopes, SecurityScheme},
8    Modify, OpenApi,
9};
10use utoipa_swagger_ui::SwaggerUi;
11
12use crate::{
13    config::auth::Config,
14    controller::{
15        areas, base_layer_image, config, drawings, guided_tours, layers, map, map_collaborators,
16        plant_layer, plantings, plants, seed, timeline, users,
17    },
18    keycloak_api,
19    model::{
20        dto::{
21            areas::{
22                AreaDto, AreaKind, NewAreaDto, UpdateAddDateAreaDto, UpdateAreaDto,
23                UpdateRemoveDateAreaDto,
24            },
25            base_layer_images::{BaseLayerImageDto, UpdateBaseLayerImageDto},
26            core::{
27                ActionDtoWrapperDeleteDrawings, ActionDtoWrapperDeleteLayer,
28                ActionDtoWrapperDeletePlantings, ActionDtoWrapperNewDrawings,
29                ActionDtoWrapperNewLayer, ActionDtoWrapperNewPlantings,
30                ActionDtoWrapperUpdateDrawings, ActionDtoWrapperUpdateLayer,
31                ActionDtoWrapperUpdatePlantings, TimelinePagePlantingsDto,
32            },
33            drawings::{
34                DrawingDto, EllipseProperties, FreeLineProperties, ImageProperties,
35                LabelTextProperties, PolygonProperties, RectangleProperties,
36                UpdateAddDateDrawingDto, UpdateNotesDrawingDto, UpdateRemoveDateDrawingDto,
37            },
38            layers::LayerDto,
39            plantings::{
40                MovePlantingDto, PlantingDto, TransformPlantingDto, UpdateAddDatePlantingDto,
41                UpdatePlantingDto, UpdatePlantingNoteDto, UpdateRemoveDatePlantingDto,
42            },
43            timeline::{TimelineDto, TimelineEntryDto},
44            ConfigDto, Coordinates, DeleteMapCollaboratorDto, GuidedToursDto, MapCollaboratorDto,
45            MapDto, NewMapCollaboratorDto, NewMapDto, NewSeedDto, PageLayerDto, PageMapDto,
46            PagePlantsSummaryDto, PageSeedDto, PlantsSummaryDto, RelationDto, RelationsDto,
47            SeedDto, UpdateGuidedToursDto, UpdateMapDto,
48        },
49        r#enum::{
50            privacy_access_control::PrivacyAccessControl, quality::Quality, quantity::Quantity,
51            shade::Shade, spatial_relation_type::SpatialRelationType,
52        },
53    },
54};
55
56/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@config`] endpoints.
57#[derive(OpenApi)]
58#[openapi(paths(config::get), components(schemas(ConfigDto)))]
59struct ConfigApiDoc;
60
61/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@seed`] endpoints.
62#[derive(OpenApi)]
63#[openapi(
64    paths(
65        seed::find,
66        seed::find_by_id,
67        seed::create,
68        seed::delete_by_id
69    ),
70    components(
71        schemas(
72            PageSeedDto,
73            SeedDto,
74            NewSeedDto,
75            Quality,
76            Quantity
77        )
78    ),
79    modifiers(&SecurityAddon)
80)]
81struct SeedApiDoc;
82
83/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@plants`] endpoints.
84#[derive(OpenApi)]
85#[openapi(
86    paths(
87        plants::find,
88        plants::find_by_id
89    ),
90    components(
91        schemas(
92            PagePlantsSummaryDto,
93            PlantsSummaryDto
94        )
95    ),
96    modifiers(&SecurityAddon)
97)]
98struct PlantsApiDoc;
99
100/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@map`] endpoints.
101#[derive(OpenApi)]
102#[openapi(
103    paths(
104        map::find,
105        map::find_by_id,
106        map::create,
107        map::update
108    ),
109    components(
110        schemas(
111            PageMapDto,
112            MapDto,
113            NewMapDto,
114            UpdateMapDto,
115            PrivacyAccessControl,
116            Coordinates
117        )
118    ),
119    modifiers(&SecurityAddon)
120)]
121struct MapApiDoc;
122
123/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@layers`] endpoints.
124#[derive(OpenApi)]
125#[openapi(
126    paths(
127        layers::find,
128        layers::find_by_id,
129        layers::create,
130        layers::delete
131    ),
132    components(
133        schemas(
134            LayerDto,
135            PageLayerDto,
136            ActionDtoWrapperNewLayer,
137            ActionDtoWrapperUpdateLayer,
138            ActionDtoWrapperDeleteLayer,
139        )
140    ),
141    modifiers(&SecurityAddon)
142)]
143struct LayerApiDoc;
144
145/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@plant_layer`] endpoints.
146#[derive(OpenApi)]
147#[openapi(
148    paths(
149        plant_layer::heatmap,
150        plant_layer::find_relations
151    ),
152    components(
153        schemas(
154            RelationsDto,
155            RelationDto,
156            SpatialRelationType
157        )
158    ),
159    modifiers(&SecurityAddon)
160)]
161struct PlantLayerApiDoc;
162
163/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@base_layer_image`] endpoints.
164#[derive(OpenApi)]
165#[openapi(
166    paths(
167        base_layer_image::find,
168        base_layer_image::create,
169        base_layer_image::update,
170        base_layer_image::delete
171    ),
172    components(
173        schemas(
174            BaseLayerImageDto,
175            UpdateBaseLayerImageDto,
176        )
177    ),
178    modifiers(&SecurityAddon)
179)]
180struct BaseLayerImagesApiDoc;
181
182/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@plantings`] endpoints.
183#[derive(OpenApi)]
184#[openapi(
185    paths(
186        plantings::find,
187        plantings::create,
188        plantings::update,
189        plantings::delete
190    ),
191    components(
192        schemas(
193            PlantingDto,
194            TimelinePagePlantingsDto,
195            UpdatePlantingDto,
196            TransformPlantingDto,
197            MovePlantingDto,
198            UpdateAddDatePlantingDto,
199            UpdateRemoveDatePlantingDto,
200            UpdatePlantingNoteDto,
201            ActionDtoWrapperNewPlantings,
202            ActionDtoWrapperUpdatePlantings,
203            ActionDtoWrapperDeletePlantings,
204            ActionDtoWrapperDeleteDrawings,
205        )
206    ),
207    modifiers(&SecurityAddon)
208)]
209struct PlantingsApiDoc;
210
211/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all area endpoints.
212#[derive(OpenApi)]
213#[openapi(
214    paths(
215        areas::find,
216        areas::create,
217        areas::update,
218        areas::delete
219    ),
220    components(
221        schemas(
222            AreaKind,
223            AreaDto,
224            NewAreaDto,
225            UpdateAreaDto,
226            UpdateAddDateAreaDto,
227            UpdateRemoveDateAreaDto,
228            Shade
229        )
230    ),
231    modifiers(&SecurityAddon)
232)]
233struct AreasApiDoc;
234
235/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@users`] endpoints.
236#[derive(OpenApi)]
237#[openapi(
238    paths(
239        users::find
240    ),
241    components(
242        schemas(
243            keycloak_api::UserDto
244        )
245    ),
246    modifiers(&SecurityAddon)
247)]
248struct UsersApiDoc;
249
250/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@guided_tours`] endpoints.
251#[derive(OpenApi)]
252#[openapi(
253    paths(
254        guided_tours::setup,
255        guided_tours::find_by_user,
256        guided_tours::update
257    ),
258    components(
259        schemas(
260            GuidedToursDto,
261            UpdateGuidedToursDto
262        )
263    ),
264    modifiers(&SecurityAddon)
265)]
266struct GuidedToursApiDoc;
267
268/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@timeline`] endpoints.
269#[derive(OpenApi)]
270#[openapi(
271    paths(
272        timeline::get_timeline,
273    ),
274    components(
275        schemas(
276            TimelineEntryDto,
277            TimelineDto
278        )
279    ),
280    modifiers(&SecurityAddon)
281)]
282struct TimelineApiDoc;
283
284/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@drawings`] endpoints.
285#[derive(OpenApi)]
286#[openapi(
287    paths(
288        drawings::find,
289        drawings::create,
290        drawings::update,
291        drawings::delete,
292    ),
293    components(
294        schemas(
295            DrawingDto,
296            RectangleProperties,
297            EllipseProperties,
298            FreeLineProperties,
299            PolygonProperties,
300            LabelTextProperties,
301            ImageProperties,
302            UpdateAddDateDrawingDto,
303            UpdateRemoveDateDrawingDto,
304            UpdateNotesDrawingDto,
305            ActionDtoWrapperNewDrawings,
306            ActionDtoWrapperUpdateDrawings,
307            ActionDtoWrapperDeleteDrawings,
308        )
309    ),
310    modifiers(&SecurityAddon)
311)]
312struct DrawingsApiDoc;
313
314/// struct used by [`utoipa`] to generate `openapi` documentation for all [`mod@map_collaborators`] endpoints.
315#[derive(OpenApi)]
316#[openapi(
317    paths(
318        map_collaborators::create,
319        map_collaborators::find,
320        map_collaborators::delete,
321    ),
322    components(
323        schemas(
324            NewMapCollaboratorDto,
325            MapCollaboratorDto,
326            DeleteMapCollaboratorDto,
327        )
328    ),
329    modifiers(&SecurityAddon)
330)]
331struct MapCollaboratorsApiDoc;
332
333/// Merges `OpenApi` and then serves it using `Swagger`.
334pub fn config(cfg: &mut web::ServiceConfig) {
335    let mut openapi = ConfigApiDoc::openapi();
336    openapi.merge(SeedApiDoc::openapi());
337    openapi.merge(PlantsApiDoc::openapi());
338    openapi.merge(MapApiDoc::openapi());
339    openapi.merge(LayerApiDoc::openapi());
340    openapi.merge(PlantLayerApiDoc::openapi());
341    openapi.merge(BaseLayerImagesApiDoc::openapi());
342    openapi.merge(PlantingsApiDoc::openapi());
343    openapi.merge(AreasApiDoc::openapi());
344    openapi.merge(UsersApiDoc::openapi());
345    openapi.merge(TimelineApiDoc::openapi());
346    openapi.merge(DrawingsApiDoc::openapi());
347    openapi.merge(GuidedToursApiDoc::openapi());
348    openapi.merge(MapCollaboratorsApiDoc::openapi());
349
350    cfg.service(SwaggerUi::new("/doc/api/swagger/ui/{_:.*}").url("/doc/api/openapi.json", openapi));
351}
352
353/// Used by [`utoipa`] for `OAuth2`.
354struct SecurityAddon;
355
356impl Modify for SecurityAddon {
357    fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
358        // We can unwrap safely since there already is components registered.
359        #[allow(clippy::unwrap_used)]
360        let components = openapi.components.as_mut().unwrap();
361
362        let config = &Config::get().openid_configuration;
363        let oauth2 = OAuth2::new([Flow::AuthorizationCode(AuthorizationCode::new(
364            config.authorization_endpoint.clone(),
365            config.token_endpoint.clone(),
366            Scopes::new(),
367        ))]);
368        components.add_security_scheme("oauth2", SecurityScheme::OAuth2(oauth2));
369    }
370}