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, 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 = plantings::update(dto.clone(), map_id, user_info.id, &pool).await?;
122
123    let action = match &dto {
124        UpdatePlantingDto::Transform(dto) => {
125            Action::new_transform_planting_action(dto, user_info.id, action_id)
126        }
127        UpdatePlantingDto::Move(dto) => {
128            Action::new_move_planting_action(dto, user_info.id, action_id)
129        }
130        UpdatePlantingDto::UpdateAddDate(dto) => {
131            Action::new_update_planting_add_date_action(dto, user_info.id, action_id)
132        }
133        UpdatePlantingDto::UpdateRemoveDate(dto) => {
134            Action::new_update_planting_remove_date_action(dto, user_info.id, action_id)
135        }
136        UpdatePlantingDto::UpdateNote(dto) => {
137            Action::new_update_planting_note_action(dto, user_info.id, action_id)
138        }
139    };
140
141    broadcaster.broadcast(map_id, action).await;
142
143    Ok(HttpResponse::Ok().json(updated_plantings))
144}
145
146/// Endpoint for deleting `Planting`s.
147///
148/// # Errors
149/// * If the connection to the database could not be established.
150#[utoipa::path(
151    context_path = "/api/maps/{map_id}/layers/plants/plantings",
152    params(
153        ("map_id" = i32, Path, description = "The id of the map the layer is on"),
154    ),
155    request_body = ActionDtoWrapperDeletePlantings,
156    responses(
157        (status = 200, description = "Delete plantings")
158    ),
159    security(
160        ("oauth2" = [])
161    )
162)]
163#[delete("")]
164pub async fn delete(
165    path: Path<i32>,
166    delete_planting: Json<ActionDtoWrapper<Vec<DeletePlantingDto>>>,
167    user_info: UserInfo,
168    pool: SharedPool,
169    broadcaster: SharedBroadcaster,
170) -> Result<HttpResponse> {
171    let map_id = path.into_inner();
172
173    let ActionDtoWrapper { action_id, dto } = delete_planting.into_inner();
174
175    plantings::delete_by_ids(dto.clone(), map_id, user_info.id, &pool).await?;
176
177    broadcaster
178        .broadcast(
179            map_id,
180            Action::new_delete_planting_action(&dto, user_info.id, action_id),
181        )
182        .await;
183
184    Ok(HttpResponse::Ok().finish())
185}