backend/controller/
areas.rs

1//! `Areas` endpoints, handling shadings, hydrologies, and soil textures.
2
3use actix_web::{
4    delete, get, patch, post,
5    web::{Json, Path, Query},
6    HttpResponse, Result,
7};
8use uuid::Uuid;
9
10use crate::{
11    config::{
12        auth::user_info::UserInfo,
13        data::{SharedBroadcaster, SharedPool},
14    },
15    model::dto::{
16        actions::Action,
17        areas::{AreaKind, AreaSearchParameters, AreaUpdate, NewAreaDto, UpdateAreaDto},
18        core::ActionDtoWrapper,
19    },
20    service::areas,
21};
22
23/// Endpoint for listing and filtering areas.
24///
25/// # Errors
26/// * If the connection to the database could not be established.
27#[utoipa::path(
28    context_path = "/api/maps/{map_id}/areas",
29    params(
30        ("map_id" = i32, Path, description = "The id of the map the layer is on"),
31        AreaSearchParameters
32    ),
33    responses(
34        (status = 200, description = "Find areas", body = Vec<AreaDto>),
35    ),
36    security(
37        ("oauth2" = [])
38    )
39)]
40#[get("")]
41pub async fn find(
42    search_params: Query<AreaSearchParameters>,
43    pool: SharedPool,
44) -> Result<HttpResponse> {
45    let response = areas::find(search_params.into_inner(), &pool).await?;
46    Ok(HttpResponse::Ok().json(response))
47}
48
49/// Endpoint for creating a new area.
50///
51/// # Errors
52/// * If the connection to the database could not be established.
53#[utoipa::path(
54    context_path = "/api/maps/{map_id}/areas",
55    params(
56        ("map_id" = i32, Path, description = "The id of the map the layer is on"),
57        ("area_kind" = AreaKind, Path, description = "The type of area"),
58    ),
59    request_body = NewAreaDto,
60    responses(
61        (status = 201, description = "Create an areas", body = AreaDto)
62    ),
63    security(
64        ("oauth2" = [])
65    )
66)]
67#[post("/{area_kind}")]
68pub async fn create(
69    path: Path<(i32, AreaKind)>,
70    new_areas: Json<ActionDtoWrapper<Vec<NewAreaDto>>>,
71    pool: SharedPool,
72    broadcaster: SharedBroadcaster,
73    user_info: UserInfo,
74) -> Result<HttpResponse> {
75    let (map_id, area_kind) = path.into_inner();
76
77    let ActionDtoWrapper { action_id, dto } = new_areas.into_inner();
78
79    let created_areas = areas::create(area_kind, dto, &pool).await?;
80
81    broadcaster
82        .broadcast(
83            map_id,
84            Action::new_create_area_action(
85                area_kind,
86                created_areas.clone(),
87                user_info.id,
88                action_id,
89            ),
90        )
91        .await;
92
93    Ok(HttpResponse::Created().json(created_areas))
94}
95
96/// Endpoint for updating an area.
97///
98/// # Errors
99/// * If the connection to the database could not be established.
100#[utoipa::path(
101    context_path = "/api/maps/{map_id}/areas",
102    params(
103        ("map_id" = i32, Path, description = "The id of the map the layer is on"),
104        ("area_kind" = AreaKind, Path, description = "The type of area"),
105    ),
106    request_body = ActionDtoWrapperUpdateAreas,
107    responses(
108        (status = 200, description = "Update multiple areas of the same kind", body = Vec<AreaDto>)
109    ),
110    security(
111        ("oauth2" = [])
112    )
113)]
114#[patch("/{area_kind}")]
115pub async fn update(
116    path: Path<(i32, AreaKind)>,
117    update_areas: Json<ActionDtoWrapper<UpdateAreaDto>>,
118    pool: SharedPool,
119    broadcaster: SharedBroadcaster,
120    user_info: UserInfo,
121) -> Result<HttpResponse> {
122    let (map_id, area_kind) = path.into_inner();
123
124    let ActionDtoWrapper { action_id, dto } = update_areas.into_inner();
125
126    let updated = areas::update(area_kind, dto.clone(), &pool).await?;
127
128    let user_id = user_info.id;
129    let action = match dto.update {
130        AreaUpdate::UpdateValue(_) => {
131            Action::new_update_area_action(area_kind, &updated, user_id, action_id)
132        }
133        AreaUpdate::UpdateAddDate(_) => {
134            Action::new_update_area_add_date_action(area_kind, &updated, user_id, action_id)
135        }
136        AreaUpdate::UpdateRemoveDate(_) => {
137            Action::new_update_area_remove_date_action(area_kind, &updated, user_id, action_id)
138        }
139        AreaUpdate::UpdateNotes(_) => {
140            Action::new_update_area_notes_action(area_kind, &updated, user_id, action_id)
141        }
142    };
143    broadcaster.broadcast(map_id, action).await;
144
145    Ok(HttpResponse::Ok().json(updated))
146}
147
148/// Endpoint for deleting an area.
149///
150/// # Errors
151/// * If the connection to the database could not be established.
152#[utoipa::path(
153    context_path = "/api/maps/{map_id}/areas",
154    params(
155        ("map_id" = i32, Path, description = "The id of the map the layer is on"),
156        ("area_kind" = AreaKind, Path, description = "The type of area"),
157    ),
158    request_body = ActionDtoWrapperDeleteAreas,
159    responses(
160        (status = 200, description = "Areas deleted")
161    ),
162    security(
163        ("oauth2" = [])
164    )
165)]
166#[delete("/{area_kind}")]
167pub async fn delete(
168    path: Path<(i32, AreaKind)>,
169    ids: Json<ActionDtoWrapper<Vec<Uuid>>>,
170    pool: SharedPool,
171    broadcaster: SharedBroadcaster,
172    user_info: UserInfo,
173) -> Result<HttpResponse> {
174    let (map_id, area_kind) = path.into_inner();
175
176    let ActionDtoWrapper { action_id, dto } = ids.into_inner();
177
178    areas::delete_by_ids(area_kind, dto.clone(), &pool).await?;
179
180    broadcaster
181        .broadcast(
182            map_id,
183            Action::new_delete_area_action(area_kind, dto, user_info.id, action_id),
184        )
185        .await;
186
187    Ok(HttpResponse::Ok().finish())
188}