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