backend/controller/
map.rs

1//! `Map` endpoints.
2
3use actix_web::web::Query;
4use actix_web::{
5    delete, get, patch, post,
6    web::{Json, Path},
7    HttpResponse, Result,
8};
9use uuid::Uuid;
10
11use crate::{
12    config::{
13        auth::user_info::UserInfo,
14        data::{SharedBroadcaster, SharedPool},
15    },
16    model::dto::{
17        actions::{Action, ActionType, UpdateMapGeometryActionPayload},
18        MapSearchParameters, NewMapDto, PageParameters, UpdateMapDto, UpdateMapGeometryDto,
19    },
20    service,
21};
22
23/// Endpoint for fetching or searching all [`Map`](crate::model::entity::Map).
24/// Search parameters are taken from the URLs query string (e.g. .../`api/maps?is_inactive=false&per_page=5`).
25/// If no page parameters are provided, the first page is returned.
26///
27/// # Errors
28/// * If the connection to the database could not be established.
29#[utoipa::path(
30    context_path = "/api/maps",
31    params(
32        MapSearchParameters,
33        PageParameters
34    ),
35    responses(
36        (status = 200, description = "Fetch or search all maps", body = PageMapDto)
37    ),
38    security(
39        ("oauth2" = [])
40    )
41)]
42#[get("")]
43pub async fn find(
44    search_query: Query<MapSearchParameters>,
45    page_query: Query<PageParameters>,
46    pool: SharedPool,
47) -> Result<HttpResponse> {
48    let response =
49        service::map::find(search_query.into_inner(), page_query.into_inner(), &pool).await?;
50    Ok(HttpResponse::Ok().json(response))
51}
52
53/// Endpoint for fetching a [`Map`](crate::model::entity::Map).
54///
55/// # Errors
56/// * If the connection to the database could not be established.
57#[utoipa::path(
58    context_path = "/api/maps",
59    responses(
60        (status = 200, description = "Fetch a map by id", body = MapDto)
61    ),
62    security(
63        ("oauth2" = [])
64    )
65)]
66#[get("/{map_id}")]
67pub async fn find_by_id(map_id: Path<i32>, pool: SharedPool) -> Result<HttpResponse> {
68    let response = service::map::find_by_id(*map_id, &pool).await?;
69    Ok(HttpResponse::Ok().json(response))
70}
71
72/// Endpoint for creating a new [`Map`](crate::model::entity::Map).
73///
74/// # Errors
75/// * If the connection to the database could not be established.
76#[utoipa::path(
77    context_path = "/api/maps",
78    request_body = NewMapDto,
79    responses(
80        (status = 201, description = "Create a new map", body = MapDto)
81    ),
82    security(
83        ("oauth2" = [])
84    )
85)]
86#[post("")]
87pub async fn create(
88    new_map_json: Json<NewMapDto>,
89    user_info: UserInfo,
90    pool: SharedPool,
91) -> Result<HttpResponse> {
92    let response = service::map::create(new_map_json.0, user_info.id, &pool).await?;
93    Ok(HttpResponse::Created().json(response))
94}
95
96/// Endpoint for updating a [`Map`](crate::model::entity::Map).
97///
98/// # Errors
99/// * If the connection to the database could not be established.
100#[utoipa::path(
101    context_path = "/api/maps",
102    request_body = UpdateMapDto,
103    responses(
104        (status = 200, description = "Update a map", body = MapDto)
105    ),
106    security(
107        ("oauth2" = [])
108    )
109)]
110#[patch("/{map_id}")]
111pub async fn update(
112    map_update_json: Json<UpdateMapDto>,
113    map_id: Path<i32>,
114    user_info: UserInfo,
115    pool: SharedPool,
116) -> Result<HttpResponse> {
117    let response =
118        service::map::update(map_update_json.0, map_id.into_inner(), user_info.id, &pool).await?;
119    Ok(HttpResponse::Ok().json(response))
120}
121/// Endpoint for updating the [`Geometry`](postgis_diesel::sql_types::Geometry) of a [`Map`](crate::model::entity::Map).
122///
123/// # Errors
124/// * If the connection to the database could not be established.
125#[utoipa::path(
126context_path = "/api/maps",
127request_body = UpdateMapDto,
128responses(
129(status = 200, description = "Update a map", body = MapDto)
130),
131security(
132("oauth2" = [])
133)
134)]
135#[patch("/{map_id}/geometry")]
136pub async fn update_geometry(
137    map_update_geometry_json: Json<UpdateMapGeometryDto>,
138    map_id: Path<i32>,
139    user_info: UserInfo,
140    pool: SharedPool,
141    broadcaster: SharedBroadcaster,
142) -> Result<HttpResponse> {
143    let map_id_inner = map_id.into_inner();
144
145    let response = service::map::update_geometry(
146        map_update_geometry_json.0.clone(),
147        map_id_inner,
148        user_info.id,
149        &pool,
150    )
151    .await?;
152
153    broadcaster
154        .broadcast(
155            map_id_inner,
156            Action {
157                action_id: Uuid::new_v4(),
158                user_id: user_info.id,
159                action: ActionType::UpdateMapGeometry(UpdateMapGeometryActionPayload::new(
160                    map_update_geometry_json.0,
161                    map_id_inner,
162                )),
163            },
164        )
165        .await;
166
167    Ok(HttpResponse::Ok().json(response))
168}
169
170/// Endpoint for soft-deleting a [`Map`](crate::model::entity::Map).
171///
172/// # Errors
173/// * If the connection to the database could not be established.
174#[utoipa::path(
175    context_path = "/api/maps",
176    responses(
177        (status = 200, description = "Delete a map by id")
178    ),
179    security(
180        ("oauth2" = [])
181    )
182)]
183#[delete("/{map_id}")]
184pub async fn delete_by_id(
185    map_id: Path<i32>,
186    user_info: UserInfo,
187    pool: SharedPool,
188) -> Result<HttpResponse> {
189    service::map::delete_by_id(*map_id, user_info.id, &pool).await?;
190    Ok(HttpResponse::Ok().finish())
191}