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