torrust_index_backend/services/
category.rs

1//! Category service.
2use std::sync::Arc;
3
4use super::user::DbUserRepository;
5use crate::databases::database::{Category, Database, Error as DatabaseError};
6use crate::errors::ServiceError;
7use crate::models::category::CategoryId;
8use crate::models::user::UserId;
9
10pub struct Service {
11    category_repository: Arc<DbCategoryRepository>,
12    user_repository: Arc<DbUserRepository>,
13}
14
15impl Service {
16    #[must_use]
17    pub fn new(category_repository: Arc<DbCategoryRepository>, user_repository: Arc<DbUserRepository>) -> Service {
18        Service {
19            category_repository,
20            user_repository,
21        }
22    }
23
24    /// Adds a new category.
25    ///
26    /// # Errors
27    ///
28    /// It returns an error if:
29    ///
30    /// * The user does not have the required permissions.
31    /// * There is a database error.
32    pub async fn add_category(&self, category_name: &str, user_id: &UserId) -> Result<i64, ServiceError> {
33        let user = self.user_repository.get_compact(user_id).await?;
34
35        // Check if user is administrator
36        // todo: extract authorization service
37        if !user.administrator {
38            return Err(ServiceError::Unauthorized);
39        }
40
41        match self.category_repository.add(category_name).await {
42            Ok(id) => Ok(id),
43            Err(e) => match e {
44                DatabaseError::CategoryAlreadyExists => Err(ServiceError::CategoryAlreadyExists),
45                _ => Err(ServiceError::DatabaseError),
46            },
47        }
48    }
49
50    /// Deletes a category.
51    ///
52    /// # Errors
53    ///
54    /// It returns an error if:
55    ///
56    /// * The user does not have the required permissions.
57    /// * There is a database error.
58    pub async fn delete_category(&self, category_name: &str, user_id: &UserId) -> Result<(), ServiceError> {
59        let user = self.user_repository.get_compact(user_id).await?;
60
61        // Check if user is administrator
62        // todo: extract authorization service
63        if !user.administrator {
64            return Err(ServiceError::Unauthorized);
65        }
66
67        match self.category_repository.delete(category_name).await {
68            Ok(_) => Ok(()),
69            Err(e) => match e {
70                DatabaseError::CategoryNotFound => Err(ServiceError::CategoryNotFound),
71                _ => Err(ServiceError::DatabaseError),
72            },
73        }
74    }
75}
76
77pub struct DbCategoryRepository {
78    database: Arc<Box<dyn Database>>,
79}
80
81impl DbCategoryRepository {
82    #[must_use]
83    pub fn new(database: Arc<Box<dyn Database>>) -> Self {
84        Self { database }
85    }
86
87    /// It returns the categories.
88    ///
89    /// # Errors
90    ///
91    /// It returns an error if there is a database error.
92    pub async fn get_all(&self) -> Result<Vec<Category>, DatabaseError> {
93        self.database.get_categories().await
94    }
95
96    /// Adds a new category.
97    ///
98    /// # Errors
99    ///
100    /// It returns an error if there is a database error.
101    pub async fn add(&self, category_name: &str) -> Result<CategoryId, DatabaseError> {
102        self.database.insert_category_and_get_id(category_name).await
103    }
104
105    /// Deletes a new category.
106    ///
107    /// # Errors
108    ///
109    /// It returns an error if there is a database error.
110    pub async fn delete(&self, category_name: &str) -> Result<(), DatabaseError> {
111        self.database.delete_category(category_name).await
112    }
113
114    /// It finds a category by name
115    ///
116    /// # Errors
117    ///
118    /// It returns an error if there is a database error.
119    pub async fn get_by_name(&self, category_name: &str) -> Result<Category, DatabaseError> {
120        self.database.get_category_from_name(category_name).await
121    }
122
123    /// It finds a category by id
124    ///
125    /// # Errors
126    ///
127    /// It returns an error if there is a database error.
128    pub async fn get_by_id(&self, category_id: &CategoryId) -> Result<Category, DatabaseError> {
129        self.database.get_category_from_id(*category_id).await
130    }
131}