backend/controller/
seed.rs

1//! `Seed` endpoints.
2
3use actix_web::web::Query;
4use actix_web::{
5    delete, get, patch, post, put,
6    web::{Json, Path},
7    HttpResponse, Result,
8};
9use uuid::Uuid;
10
11use crate::service::map_access_control::check_creation_permission;
12use crate::{
13    config::{
14        auth::user_info::UserInfo,
15        data::{SharedBroadcaster, SharedPool},
16    },
17    model::dto::{
18        actions::{Action, ActionType, UpdatePlantingAdditionalNamePayload},
19        ArchiveSeedDto, NewSeedDto, PageParameters, SeedSearchParameters,
20    },
21    service,
22};
23
24/// Endpoint for fetching all [`SeedDto`](crate::model::dto::SeedDto).
25/// If no page parameters are provided, the first page is returned.
26/// Seeds are ordered using their `use_by` date in an ascending fashion.
27///
28/// By default, archived seeds will not be returned.
29/// This behaviour can be changed using `search_parameters`.
30///
31/// # Errors
32/// * If the connection to the database could not be established.
33#[utoipa::path(
34    context_path = "/api/seeds",
35    params(
36        SeedSearchParameters,
37        PageParameters
38    ),
39    responses(
40        (status = 200, description = "Fetch all seeds", body = PageSeedDto)
41    ),
42    security(
43        ("oauth2" = [])
44    )
45)]
46#[get("")]
47pub async fn find(
48    search_query: Query<SeedSearchParameters>,
49    page_query: Query<PageParameters>,
50    user_info: UserInfo,
51    pool: SharedPool,
52) -> Result<HttpResponse> {
53    let response = service::seed::find(
54        search_query.into_inner(),
55        page_query.into_inner(),
56        user_info.id,
57        &pool,
58    )
59    .await?;
60    Ok(HttpResponse::Ok().json(response))
61}
62
63/// Endpoint for fetching a [`Seed`](crate::model::entity::Seed).
64///
65/// # Errors
66/// * If the connection to the database could not be established.
67#[utoipa::path(
68    context_path = "/api/seeds",
69    responses(
70        (status = 200, description = "Fetch seed by id", body = SeedDto)
71    ),
72    security(
73        ("oauth2" = [])
74    )
75)]
76#[get("/{id}")]
77pub async fn find_by_id(
78    id: Path<i32>,
79    user_info: UserInfo,
80    pool: SharedPool,
81) -> Result<HttpResponse> {
82    let response = service::seed::find_by_id((*id).into(), user_info.id, &pool).await?;
83    Ok(HttpResponse::Ok().json(response))
84}
85
86/// Endpoint for creating a new [`Seed`](crate::model::entity::Seed).
87///
88/// # Errors
89/// * If the connection to the database could not be established.
90#[utoipa::path(
91    context_path = "/api/seeds",
92    request_body = NewSeedDto,
93    responses(
94        (status = 201, description = "Create a seed", body = SeedDto)
95    ),
96    security(
97        ("oauth2" = [])
98    )
99)]
100#[post("")]
101pub async fn create(
102    new_seed_json: Json<NewSeedDto>,
103    user_info: UserInfo,
104    pool: SharedPool,
105) -> Result<HttpResponse> {
106    check_creation_permission(&user_info).await?;
107    let response = service::seed::create(new_seed_json.0, user_info.id, &pool).await?;
108    Ok(HttpResponse::Created().json(response))
109}
110
111/// Endpoint for deleting a [`Seed`](crate::model::entity::Seed).
112///
113/// # Errors
114/// * If the connection to the database could not be established.
115#[utoipa::path(
116    context_path = "/api/seeds",
117    responses(
118        (status = 200, description = "Delete a seed", body = String)
119    ),
120    security(
121        ("oauth2" = [])
122    )
123)]
124#[delete("/{id}")]
125pub async fn delete_by_id(
126    path: Path<i32>,
127    user_info: UserInfo,
128    pool: SharedPool,
129) -> Result<HttpResponse> {
130    service::seed::delete_by_id((*path).into(), user_info.id, &pool).await?;
131    Ok(HttpResponse::Ok().json(""))
132}
133
134/// Endpoint for editing a [`Seed`](crate::model::entity::Seed).
135///
136/// # Errors
137/// * If the connection to the database could not be established.
138#[put("/{id}")]
139pub async fn edit_by_id(
140    id: Path<i32>,
141    edit_seed_json: Json<NewSeedDto>,
142    user_info: UserInfo,
143    pool: SharedPool,
144    broadcaster: SharedBroadcaster,
145) -> Result<HttpResponse> {
146    let response = service::seed::edit((*id).into(), user_info.id, edit_seed_json.0, &pool).await?;
147    let affected_plantings = service::plantings::find_by_seed_id((*id).into(), &pool);
148
149    for planting in affected_plantings.await? {
150        broadcaster
151            .broadcast_all_maps(Action {
152                action_id: Uuid::now_v7(),
153                user_id: user_info.id,
154                action: ActionType::UpdatePlantingAdditionalName(
155                    UpdatePlantingAdditionalNamePayload::new(
156                        &planting,
157                        Some(response.name.clone()),
158                    ),
159                ),
160            })
161            .await;
162    }
163
164    Ok(HttpResponse::Accepted().json(response))
165}
166
167/// Endpoint archiving/unarchiving a [`Seed`](crate::model::entity::Seed).
168/// A timestamp will be recorded when the seed is first archived.
169///
170/// # Errors
171/// * If the connection to the database could not be established.
172#[patch("/{id}/archive")]
173pub async fn archive(
174    id: Path<i32>,
175    archive_seed_json: Json<ArchiveSeedDto>,
176    user_info: UserInfo,
177    pool: SharedPool,
178) -> Result<HttpResponse> {
179    let response =
180        service::seed::archive((*id).into(), user_info.id, archive_seed_json.0, &pool).await?;
181    Ok(HttpResponse::Accepted().json(response))
182}