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_tour, 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            CompleteGuidedTourStepDto, CompleteGuidedTourStepsDto, ConfigDto, Coordinates,
45            DeleteMapCollaboratorDto, GuidedTourDto, MapCollaboratorDto, MapDto,
46            NewMapCollaboratorDto, NewMapDto, NewSeedDto, PageLayerDto, PageMapDto,
47            PagePlantsSummaryDto, PageSeedDto, PlantsSummaryDto, RelationDto, RelationsDto,
48            SeedDto, UpdateMapDto,
49        },
50        r#enum::{
51            privacy_access_control::PrivacyAccessControl, quality::Quality, quantity::Quantity,
52            shade::Shade, 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            PrivacyAccessControl,
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::find
241    ),
242    components(
243        schemas(
244            keycloak_api::UserDto
245        )
246    ),
247    modifiers(&SecurityAddon)
248)]
249struct UsersApiDoc;
250
251/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@guided_tour`] endpoints.
252#[derive(OpenApi)]
253#[openapi(
254    paths(
255        guided_tour::pause,
256        guided_tour::find_by_user,
257        guided_tour::complete_step,
258        guided_tour::complete_steps_bulk,
259    ),
260    components(
261        schemas(
262            GuidedTourDto,
263            CompleteGuidedTourStepDto,
264            CompleteGuidedTourStepsDto
265        )
266    ),
267    modifiers(&SecurityAddon)
268)]
269struct GuidedTourApiDoc;
270
271/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@timeline`] endpoints.
272#[derive(OpenApi)]
273#[openapi(
274    paths(
275        timeline::get_timeline,
276    ),
277    components(
278        schemas(
279            TimelineEntryDto,
280            TimelineDto
281        )
282    ),
283    modifiers(&SecurityAddon)
284)]
285struct TimelineApiDoc;
286
287/// Struct used by [`utoipa`] to generate `OpenApi` documentation for all [`mod@drawings`] endpoints.
288#[derive(OpenApi)]
289#[openapi(
290    paths(
291        drawings::find,
292        drawings::create,
293        drawings::update,
294        drawings::delete,
295    ),
296    components(
297        schemas(
298            DrawingDto,
299            RectangleProperties,
300            EllipseProperties,
301            FreeLineProperties,
302            PolygonProperties,
303            LabelTextProperties,
304            ImageProperties,
305            UpdateAddDateDrawingDto,
306            UpdateRemoveDateDrawingDto,
307            UpdateNotesDrawingDto,
308            ActionDtoWrapperNewDrawings,
309            ActionDtoWrapperUpdateDrawings,
310            ActionDtoWrapperDeleteDrawings,
311        )
312    ),
313    modifiers(&SecurityAddon)
314)]
315struct DrawingsApiDoc;
316
317/// struct used by [`utoipa`] to generate `openapi` documentation for all [`mod@map_collaborators`] endpoints.
318#[derive(OpenApi)]
319#[openapi(
320    paths(
321        map_collaborators::create,
322        map_collaborators::find,
323        map_collaborators::delete,
324    ),
325    components(
326        schemas(
327            NewMapCollaboratorDto,
328            MapCollaboratorDto,
329            DeleteMapCollaboratorDto,
330        )
331    ),
332    modifiers(&SecurityAddon)
333)]
334struct MapCollaboratorsApiDoc;
335
336/// Merges `OpenApi` and then serves it using `Swagger`.
337pub fn config(cfg: &mut web::ServiceConfig) {
338    let mut openapi = ConfigApiDoc::openapi();
339    openapi.merge(SeedApiDoc::openapi());
340    openapi.merge(PlantsApiDoc::openapi());
341    openapi.merge(MapApiDoc::openapi());
342    openapi.merge(LayerApiDoc::openapi());
343    openapi.merge(PlantLayerApiDoc::openapi());
344    openapi.merge(BaseLayerImagesApiDoc::openapi());
345    openapi.merge(PlantingsApiDoc::openapi());
346    openapi.merge(AreasApiDoc::openapi());
347    openapi.merge(UsersApiDoc::openapi());
348    openapi.merge(TimelineApiDoc::openapi());
349    openapi.merge(DrawingsApiDoc::openapi());
350    openapi.merge(GuidedTourApiDoc::openapi());
351    openapi.merge(MapCollaboratorsApiDoc::openapi());
352
353    cfg.service(SwaggerUi::new("/doc/api/swagger/ui/{_:.*}").url("/doc/api/openapi.json", openapi));
354}
355
356/// Used by [`utoipa`] for `OAuth2`.
357struct SecurityAddon;
358
359impl Modify for SecurityAddon {
360    fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
361        // We can unwrap safely since there already is components registered.
362        #[allow(clippy::unwrap_used)]
363        let components = openapi.components.as_mut().unwrap();
364
365        let config = &Config::get().openid_configuration;
366        let oauth2 = OAuth2::new([Flow::AuthorizationCode(AuthorizationCode::new(
367            config.authorization_endpoint.clone(),
368            config.token_endpoint.clone(),
369            Scopes::new(),
370        ))]);
371        components.add_security_scheme("oauth2", SecurityScheme::OAuth2(oauth2));
372    }
373}