backend/controller/
plantings.rs

1//! `Planting` endpoints.
2
3use actix_web::{
4    delete, get, patch, post,
5    web::{Json, Path, Query},
6    HttpResponse, Result,
7};
8
9use crate::{
10    config::{
11        auth::user_info::UserInfo,
12        data::{SharedBroadcaster, SharedPool},
13    },
14    model::dto::{
15        actions::Action,
16        core::ActionDtoWrapper,
17        plantings::{DeletePlantingDto, PlantingDto, PlantingSearchParameters, UpdatePlantingDto},
18    },
19    service::plantings,
20};
21
22/// Endpoint for listing and filtering `Planting`s.
23///
24/// # Errors
25/// * If the connection to the database could not be established.
26#[utoipa::path(
27    context_path = "/api/maps/{map_id}/layers/plants/plantings",
28    params(
29        ("map_id" = i32, Path, description = "The id of the map the layer is on"),
30        PlantingSearchParameters
31    ),
32    responses(
33        (status = 200, description = "Find plantings", body = TimelinePagePlantingsDto)
34    ),
35    security(
36        ("oauth2" = [])
37    )
38)]
39#[get("")]
40pub async fn find(
41    // define here, even though it's not used.
42    // So clients need to provide the map_id and it is checked.
43    _map_id: Path<i32>,
44    search_params: Query<PlantingSearchParameters>,
45    pool: SharedPool,
46) -> Result<HttpResponse> {
47    let response = plantings::find(search_params.into_inner(), &pool).await?;
48    Ok(HttpResponse::Ok().json(response))
49}
50
51/// Endpoint for creating new `Planting`s.
52///
53/// # Errors
54/// * If the connection to the database could not be established.
55#[utoipa::path(
56    context_path = "/api/maps/{map_id}/layers/plants/plantings",
57    params(
58        ("map_id" = i32, Path, description = "The id of the map the layer is on"),
59    ),
60    request_body = ActionDtoWrapperNewPlantings,
61    responses(
62        (status = 201, description = "Create plantings", body = Vec<PlantingDto>)
63    ),
64    security(
65        ("oauth2" = [])
66    )
67)]
68#[post("")]
69pub async fn create(
70    path: Path<i32>,
71    new_plantings: Json<ActionDtoWrapper<Vec<PlantingDto>>>,
72    user_info: UserInfo,
73    pool: SharedPool,
74    broadcaster: SharedBroadcaster,
75) -> Result<HttpResponse> {
76    let map_id = path.into_inner();
77
78    let ActionDtoWrapper { action_id, dto } = new_plantings.into_inner();
79
80    let created_plantings = plantings::create(dto, map_id.into(), user_info.id, &pool).await?;
81
82    broadcaster
83        .broadcast(
84            map_id,
85            Action::new_create_planting_action(created_plantings.clone(), user_info.id, action_id),
86        )
87        .await;
88
89    Ok(HttpResponse::Created().json(created_plantings))
90}
91
92/// Endpoint for updating `Planting`s.
93///
94/// # Errors
95/// * If the connection to the database could not be established.
96#[utoipa::path(
97    context_path = "/api/maps/{map_id}/layers/plants/plantings",
98    params(
99        ("map_id" = i32, Path, description = "The id of the map the layer is on"),
100    ),
101    request_body = ActionDtoWrapperUpdatePlantings,
102    responses(
103        (status = 200, description = "Update plantings", body = Vec<PlantingDto>)
104    ),
105    security(
106        ("oauth2" = [])
107    )
108)]
109#[patch("")]
110pub async fn update(
111    path: Path<i32>,
112    update_planting: Json<ActionDtoWrapper<UpdatePlantingDto>>,
113    user_info: UserInfo,
114    pool: SharedPool,
115    broadcaster: SharedBroadcaster,
116) -> Result<HttpResponse> {
117    let map_id = path.into_inner();
118
119    let ActionDtoWrapper { action_id, dto } = update_planting.into_inner();
120
121    let updated_plantings =
122        plantings::update(dto.clone(), map_id.into(), user_info.id, &pool).await?;
123
124    let action = match &dto {
125        UpdatePlantingDto::Transform(dto) => {
126            Action::new_transform_planting_action(dto, user_info.id, action_id)
127        }
128        UpdatePlantingDto::Move(dto) => {
129            Action::new_move_planting_action(dto, user_info.id, action_id)
130        }
131        UpdatePlantingDto::UpdateAddDate(dto) => {
132            Action::new_update_planting_add_date_action(dto, user_info.id, action_id)
133        }
134        UpdatePlantingDto::UpdateRemoveDate(dto) => {
135            Action::new_update_planting_remove_date_action(dto, user_info.id, action_id)
136        }
137        UpdatePlantingDto::UpdateNote(dto) => {
138            Action::new_update_planting_note_action(dto, user_info.id, action_id)
139        }
140    };
141
142    broadcaster.broadcast(map_id, action).await;
143
144    Ok(HttpResponse::Ok().json(updated_plantings))
145}
146
147/// Endpoint for deleting `Planting`s.
148///
149/// # Errors
150/// * If the connection to the database could not be established.
151#[utoipa::path(
152    context_path = "/api/maps/{map_id}/layers/plants/plantings",
153    params(
154        ("map_id" = i32, Path, description = "The id of the map the layer is on"),
155    ),
156    request_body = ActionDtoWrapperDeletePlantings,
157    responses(
158        (status = 200, description = "Delete plantings")
159    ),
160    security(
161        ("oauth2" = [])
162    )
163)]
164#[delete("")]
165pub async fn delete(
166    path: Path<i32>,
167    delete_planting: Json<ActionDtoWrapper<Vec<DeletePlantingDto>>>,
168    user_info: UserInfo,
169    pool: SharedPool,
170    broadcaster: SharedBroadcaster,
171) -> Result<HttpResponse> {
172    let map_id = path.into_inner();
173
174    let ActionDtoWrapper { action_id, dto } = delete_planting.into_inner();
175
176    plantings::delete_by_ids(dto.clone(), map_id.into(), user_info.id, &pool).await?;
177
178    broadcaster
179        .broadcast(
180            map_id,
181            Action::new_delete_planting_action(&dto, user_info.id, action_id),
182        )
183        .await;
184
185    Ok(HttpResponse::Ok().finish())
186}