backend/controller/
layers.rs

1//! Layer endpoints.
2
3use actix_web::{
4    delete, get, post, put,
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, ActionType},
17        core::{
18            ActionDtoWrapper, ActionDtoWrapperDeleteLayer, ActionDtoWrapperNewLayer,
19            ActionDtoWrapperUpdateLayer,
20        },
21        layers::LayerSearchParameters,
22    },
23    service::{
24        layer,
25        map_access_control::{check_permissions, AccessRights},
26    },
27};
28
29/// Endpoint for searching layers. Layers are returned in order of their
30/// `order_index`.
31///
32/// # Errors
33/// * If the connection to the database could not be established.
34/// * If the current user does not have the appropriate permissions.
35#[utoipa::path(
36    context_path = "/api/maps/{map_id}/layers",
37    params(
38        ("map_id" = i32, Path, description = "The id of the map the layer is on"),
39        LayerSearchParameters,
40    ),
41    responses(
42        (status = 200, description = "Search layers", body = VecLayerDto)
43    ),
44    security(
45        ("oauth2" = [])
46    )
47)]
48#[get("")]
49pub async fn find(
50    search_query: Query<LayerSearchParameters>,
51    map_id: Path<i32>,
52    pool: SharedPool,
53    user_info: UserInfo,
54) -> Result<HttpResponse> {
55    let mut search_params = search_query.into_inner();
56    let id = map_id.into_inner();
57    search_params.map_id = Some(id);
58
59    check_permissions(id, &pool, user_info, AccessRights::Read).await?;
60
61    let response = layer::find(search_params, &pool).await?;
62    Ok(HttpResponse::Ok().json(response))
63}
64
65/// Endpoint for fetching a layer by its id.
66///
67/// # Errors
68/// * If the connection to the database could not be established.
69#[utoipa::path(
70    context_path = "/api/maps/{map_id}/layers",
71    params(
72        ("map_id" = i32, Path, description = "The id of the map the layer is on"),
73    ),
74    responses(
75        (status = 200, description = "Fetch layer by id", body = LayerDto)
76    ),
77    security(
78        ("oauth2" = [])
79    )
80)]
81#[get("/{id}")]
82pub async fn find_by_id(path: Path<(i32, Uuid)>, pool: SharedPool) -> Result<HttpResponse> {
83    let (_, id) = path.into_inner();
84    let response = layer::find_by_id(id, &pool).await?;
85    Ok(HttpResponse::Ok().json(response))
86}
87
88/// Endpoint for creating a new layer.
89///
90/// # Errors
91/// * If the connection to the database could not be established.
92/// * If the current user does not have the appropriate permissions.
93#[utoipa::path(
94    context_path = "/api/maps/{map_id}/layers",
95    params(
96        ("map_id" = i32, Path, description = "The id of the map the layer is on"),
97    ),
98    request_body = ActionDtoWrapperUpdateLayer,
99    responses(
100        (status = 201, description = "Create a plant layer", body = LayerDto)
101    ),
102    security(
103        ("oauth2" = [])
104    )
105)]
106#[post("")]
107pub async fn create(
108    path: Path<i32>,
109    new_layer: Json<ActionDtoWrapperNewLayer>,
110    pool: SharedPool,
111    broadcaster: SharedBroadcaster,
112    user_info: UserInfo,
113) -> Result<HttpResponse> {
114    let ActionDtoWrapper { action_id, dto } = new_layer.into_inner();
115    let map_id = path.into_inner();
116    let user_id = user_info.id;
117
118    check_permissions(map_id, &pool, user_info, AccessRights::Write).await?;
119
120    let dto = layer::create(map_id, dto, &pool).await?;
121
122    broadcaster
123        .broadcast(
124            map_id,
125            Action {
126                action_id,
127                user_id,
128                action: ActionType::CreateLayer(dto.clone()),
129            },
130        )
131        .await;
132
133    Ok(HttpResponse::Created().json(dto))
134}
135
136/// Endpoint for updating layers.
137///
138/// # Errors
139/// * If the connection to the database could not be established.
140/// * If the current user does not have the appropriate permissions.
141#[utoipa::path(
142    context_path = "/api/maps/{map_id}/layers",
143    params(
144        ("map_id" = i32, Path, description = "The id of the map"),
145    ),
146    request_body = ActionDtoWrapperUpdateLayer,
147    responses(
148        (status = 200, description = "Layers have been reordered")
149    ),
150    security(
151        ("oauth2" = [])
152    )
153)]
154#[put("")]
155pub async fn update(
156    path: Path<i32>,
157    update: Json<ActionDtoWrapperUpdateLayer>,
158    pool: SharedPool,
159    broadcaster: SharedBroadcaster,
160    user_info: UserInfo,
161) -> Result<HttpResponse> {
162    let ActionDtoWrapper { action_id, dto } = update.into_inner();
163    let map_id = path.into_inner();
164    let user_id = user_info.id;
165
166    check_permissions(map_id, &pool, user_info, AccessRights::Write).await?;
167
168    let action = layer::update(map_id, dto.clone(), &pool).await?;
169
170    broadcaster
171        .broadcast(
172            map_id,
173            Action {
174                action_id,
175                user_id,
176                action,
177            },
178        )
179        .await;
180
181    Ok(HttpResponse::Ok().finish())
182}
183
184/// Endpoint for deleting a layer.
185/// Layers are soft-deleted and marked as deleted.
186///
187/// # Errors
188/// * If the connection to the database could not be established.
189/// * If the current user does not have the appropriate permissions.
190#[utoipa::path(
191    context_path = "/api/maps/{map_id}/layers",
192    params(
193        ("map_id" = i32, Path, description = "The id of the map the layer is on"),
194    ),
195    request_body = ActionDtoWrapperDeleteLayer,
196    responses(
197        (status = 200, description = "Delete a layer")
198    ),
199    security(
200        ("oauth2" = [])
201    )
202)]
203#[delete("")]
204pub async fn delete(
205    path: Path<i32>,
206    delete_layer: Json<ActionDtoWrapperDeleteLayer>,
207    pool: SharedPool,
208    broadcaster: SharedBroadcaster,
209    user_info: UserInfo,
210) -> Result<HttpResponse> {
211    let ActionDtoWrapper { action_id, dto } = delete_layer.into_inner();
212    let map_id = path.into_inner();
213    let user_id = user_info.id;
214    let layer_id = dto.id;
215
216    check_permissions(map_id, &pool, user_info, AccessRights::Write).await?;
217
218    layer::delete_by_id(map_id, layer_id, &pool).await?;
219
220    broadcaster
221        .broadcast(
222            map_id,
223            Action {
224                action_id,
225                user_id,
226                action: ActionType::DeleteLayer(layer_id),
227            },
228        )
229        .await;
230
231    Ok(HttpResponse::Ok().finish())
232}