backend/service/
map_collaborator.rs

1use actix_http::StatusCode;
2use uuid::Uuid;
3
4use crate::{
5    config::data::{SharedHttpClient, SharedKeycloakApi, SharedPool},
6    error::ServiceError,
7    model::{
8        dto::{DeleteMapCollaboratorDto, MapCollaboratorDto, NewMapCollaboratorDto},
9        entity::{Map, MapCollaborator},
10    },
11    service::users,
12};
13
14/// The maximum number of collaborators a map can have.
15const MAX_COLLABORATORS: usize = 30;
16
17/// Get all collaborators for a map.
18///
19/// # Errors
20/// * If the connection to the database could not be established.
21/// * If the connection to the Keycloak API could not be established.
22pub async fn get_all(
23    map_id: i32,
24    pool: &SharedPool,
25    keycloak_api: &SharedKeycloakApi,
26    http_client: &SharedHttpClient,
27) -> Result<Vec<MapCollaboratorDto>, ServiceError> {
28    let mut conn = pool.get().await?;
29    // Check if map exists
30    let _ = Map::find_by_id(map_id, &mut conn).await?;
31
32    let collaborators = MapCollaborator::find_by_map_id(map_id, &mut conn).await?;
33
34    let collaborator_ids = collaborators.iter().map(|c| c.user_id).collect::<Vec<_>>();
35    let users = users::find_by_ids(collaborator_ids, keycloak_api, http_client).await?;
36
37    let dtos = collaborators
38        .into_iter()
39        .zip(users.into_iter())
40        .map(|(c, u)| MapCollaboratorDto::from((c, u)))
41        .collect::<Vec<MapCollaboratorDto>>();
42
43    Ok(dtos)
44}
45
46/// Create a new collaborator for a map.
47///
48/// # Errors
49/// * If the user tries to add themselves as a collaborator.
50/// * If the user is not the creator of the map.
51/// * If the map already has 30 collaborators.
52/// * If the connection to the database could not be established.
53/// * If the connection to the Keycloak API could not be established.
54pub async fn create(
55    map_and_collaborator: (i32, NewMapCollaboratorDto),
56    user_id: Uuid,
57    pool: &SharedPool,
58    keycloak_api: &SharedKeycloakApi,
59    http_client: &SharedHttpClient,
60) -> Result<MapCollaboratorDto, ServiceError> {
61    let (map_id, ref new_map_collaborator) = map_and_collaborator;
62
63    if new_map_collaborator.user_id == user_id {
64        return Err(ServiceError::new(
65            StatusCode::BAD_REQUEST,
66            "You cannot add yourself as a collaborator.",
67        ));
68    }
69
70    let mut conn = pool.get().await?;
71
72    let map = Map::find_by_id(map_id, &mut conn).await?;
73
74    if map.created_by != user_id {
75        return Err(ServiceError::new(
76            StatusCode::FORBIDDEN,
77            "You are not the creator of this map.",
78        ));
79    }
80
81    let current_collaborators = MapCollaborator::find_by_map_id(map_id, &mut conn).await?;
82
83    if current_collaborators.len() >= MAX_COLLABORATORS {
84        return Err(ServiceError::new(
85            StatusCode::BAD_REQUEST,
86            &format!("A map can have at most {MAX_COLLABORATORS} collaborators."),
87        ));
88    }
89
90    let collaborator_user =
91        users::find_by_id(new_map_collaborator.user_id, keycloak_api, http_client).await?;
92
93    let collaborator = MapCollaborator::create(map_and_collaborator, &mut conn).await?;
94
95    Ok(MapCollaboratorDto::from((collaborator, collaborator_user)))
96}
97
98/// Remove a collaborator from a map.
99///
100/// # Errors
101/// * If the user is not the creator of the map.
102/// * If the connection to the database could not be established.
103/// * If the connection to the Keycloak API could not be established.
104pub async fn delete(
105    map_and_dto: (i32, DeleteMapCollaboratorDto),
106    user_id: Uuid,
107    pool: &SharedPool,
108) -> Result<(), ServiceError> {
109    let (map_id, _) = map_and_dto;
110
111    let mut conn = pool.get().await?;
112
113    let map = Map::find_by_id(map_id, &mut conn).await?;
114
115    if map.created_by != user_id {
116        return Err(ServiceError::new(
117            StatusCode::FORBIDDEN,
118            "You are not the creator of this map.",
119        ));
120    }
121
122    MapCollaborator::delete(map_and_dto, &mut conn).await?;
123
124    Ok(())
125}