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