backend/service/
layer.rs

1//! Service layer for layers.
2
3use diesel_async::AsyncPgConnection;
4use reqwest::StatusCode;
5use uuid::Uuid;
6
7use crate::model::dto::actions::{ActionType, RestoreDrawingLayerActionPayload};
8use crate::model::dto::drawings::DrawingDto;
9use crate::model::dto::layers::UpdateLayerDto;
10use crate::model::entity::drawings::Drawing;
11use crate::model::entity::Map;
12use crate::model::r#enum::layer_type::LayerType;
13use crate::{
14    config::data::SharedPool,
15    error::ServiceError,
16    model::{
17        dto::layers::{LayerDto, LayerSearchParameters},
18        entity::layers::Layer,
19    },
20};
21
22/// Search layers from the database.
23///
24/// # Errors
25/// If the connection to the database could not be established.
26pub async fn find(
27    search_parameters: LayerSearchParameters,
28    pool: &SharedPool,
29) -> Result<Vec<LayerDto>, ServiceError> {
30    let mut conn = pool.get().await?;
31    let result = Layer::find(search_parameters, &mut conn).await?;
32    Ok(result)
33}
34
35/// Find a layer by id in the database.
36///
37/// # Errors
38/// If the connection to the database could not be established.
39pub async fn find_by_id(id: Uuid, pool: &SharedPool) -> Result<LayerDto, ServiceError> {
40    let mut conn = pool.get().await?;
41    let result = Layer::find_by_id(id, &mut conn).await?;
42    Ok(result.into())
43}
44
45/// Create a new layer in the database.
46///
47/// # Errors
48/// If the connection to the database could not be established.
49pub async fn create(
50    map_id: i32,
51    new_layer: LayerDto,
52    pool: &SharedPool,
53) -> Result<LayerDto, ServiceError> {
54    let mut conn = pool.get().await?;
55    _ = Map::find_by_id(map_id, &mut conn).await?;
56    let result = Layer::create(map_id, new_layer, &mut conn).await?;
57    Ok(result)
58}
59
60/// Helper that returns an error if the given layer is not on the map.
61///
62/// # Errors
63/// If the connection to the database could not be established.
64async fn assert_layer_on_map(
65    map_id: i32,
66    layer_id: Uuid,
67    conn: &mut AsyncPgConnection,
68) -> Result<(), ServiceError> {
69    let layer = Layer::find_by_id(layer_id, conn).await?;
70    if layer.map_id != map_id {
71        return Err(ServiceError::new(
72            StatusCode::CONFLICT,
73            "map_id of path and dto doesn't match",
74        ));
75    }
76    Ok(())
77}
78
79/// Helper that returns an error in case `layer_ids` isn't exactly
80/// the set of all non-deleted layers on the map.
81///
82/// # Errors
83/// If the connection to the database could not be established.
84async fn assert_all_layers_on_map(
85    map_id: i32,
86    layer_ids: &[Uuid],
87    conn: &mut AsyncPgConnection,
88) -> Result<(), ServiceError> {
89    let layer_ids_in_map: Vec<Uuid> = Layer::find(
90        LayerSearchParameters {
91            map_id: Some(map_id),
92            type_: None,
93            is_alternative: None,
94            only_non_deleted: Some(()),
95        },
96        conn,
97    )
98    .await?
99    .into_iter()
100    .map(|layer| layer.id)
101    .collect();
102
103    for layer_id in layer_ids {
104        if !layer_ids_in_map.contains(layer_id) {
105            return Err(ServiceError::new(
106                StatusCode::CONFLICT,
107                "map_id of path and dto doesn't match",
108            ));
109        }
110    }
111    Ok(())
112}
113
114/// Update layers. Available updates are:
115///  - renaming of one layer
116///  - reordering of multiple layers
117///  - restoring of one layer that is makred deleted
118///
119/// # Errors
120/// If the connection to the database could not be established.
121pub async fn update(
122    map_id: i32,
123    update: UpdateLayerDto,
124    pool: &SharedPool,
125) -> Result<ActionType, ServiceError> {
126    let mut conn = pool.get().await?;
127
128    let action = match update {
129        UpdateLayerDto::Rename(dto) => {
130            assert_layer_on_map(map_id, dto.id, &mut conn).await?;
131            Layer::rename(dto.clone(), &mut conn).await?;
132            ActionType::RenameLayer(dto)
133        }
134        UpdateLayerDto::Reorder(new_order) => {
135            assert_all_layers_on_map(map_id, &new_order, &mut conn).await?;
136            Layer::reorder(new_order.clone(), &mut conn).await?;
137            ActionType::ReorderLayers(new_order)
138        }
139        UpdateLayerDto::RestoreDrawingLayer(dto) => {
140            assert_layer_on_map(map_id, dto.id, &mut conn).await?;
141            let layer = Layer::find_by_id(dto.id, &mut conn).await?;
142            if layer.marked_deleted.is_none() {
143                return Err(ServiceError::new(
144                    StatusCode::CONFLICT,
145                    "layer is not marked deleted",
146                ));
147            }
148            Layer::restore(map_id, dto.id, &mut conn).await?;
149            let drawings = Drawing::find_in_layer(dto.id, &mut conn).await?;
150            let drawing_dtos = drawings
151                .into_iter()
152                .map(DrawingDto::try_from)
153                .collect::<Result<Vec<DrawingDto>, ServiceError>>()?;
154            ActionType::RestoreDrawingLayer(RestoreDrawingLayerActionPayload::new(
155                dto.id,
156                drawing_dtos,
157            ))
158        }
159    };
160    Ok(action)
161}
162
163/// Delete the layer in the database.
164///
165/// # Errors
166/// If the connection to the database could not be established.
167pub async fn delete_by_id(
168    map_id: i32,
169    layer_id: Uuid,
170    pool: &SharedPool,
171) -> Result<(), ServiceError> {
172    let mut conn = pool.get().await?;
173    let layer = Layer::find_by_id(layer_id, &mut conn).await?;
174    if layer.type_ != LayerType::Drawing {
175        return Err(ServiceError::new(
176            StatusCode::FORBIDDEN,
177            "can only delete drawing layer",
178        ));
179    }
180    Layer::delete(map_id, layer_id, &mut conn).await?;
181    Ok(())
182}