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