Skip to content

Commit f693a02

Browse files
committed
refactor: [#187] extract tag::Service
See #157.
1 parent b97698a commit f693a02

File tree

11 files changed

+146
-57
lines changed

11 files changed

+146
-57
lines changed

src/app.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::config::Configuration;
1111
use crate::databases::database;
1212
use crate::services::authentication::{DbUserAuthenticationRepository, JsonWebToken, Service};
1313
use crate::services::category::{self, DbCategoryRepository};
14+
use crate::services::tag::{self, DbTagRepository};
1415
use crate::services::torrent::{
1516
DbTorrentAnnounceUrlRepository, DbTorrentFileRepository, DbTorrentInfoRepository, DbTorrentListingGenerator,
1617
DbTorrentRepository, DbTorrentTagRepository,
@@ -58,6 +59,7 @@ pub async fn run(configuration: Configuration, api_implementation: &Implementati
5859

5960
// Repositories
6061
let category_repository = Arc::new(DbCategoryRepository::new(database.clone()));
62+
let tag_repository = Arc::new(DbTagRepository::new(database.clone()));
6163
let user_repository = Arc::new(DbUserRepository::new(database.clone()));
6264
let user_authentication_repository = Arc::new(DbUserAuthenticationRepository::new(database.clone()));
6365
let user_profile_repository = Arc::new(DbUserProfileRepository::new(database.clone()));
@@ -76,6 +78,7 @@ pub async fn run(configuration: Configuration, api_implementation: &Implementati
7678
let mailer_service = Arc::new(mailer::Service::new(configuration.clone()).await);
7779
let image_cache_service: Arc<ImageCacheService> = Arc::new(ImageCacheService::new(configuration.clone()).await);
7880
let category_service = Arc::new(category::Service::new(category_repository.clone(), user_repository.clone()));
81+
let tag_service = Arc::new(tag::Service::new(tag_repository.clone(), user_repository.clone()));
7982
let proxy_service = Arc::new(proxy::Service::new(image_cache_service.clone(), user_repository.clone()));
8083
let settings_service = Arc::new(settings::Service::new(configuration.clone(), user_repository.clone()));
8184
let torrent_index = Arc::new(torrent::Index::new(
@@ -123,6 +126,7 @@ pub async fn run(configuration: Configuration, api_implementation: &Implementati
123126
mailer_service,
124127
image_cache_service,
125128
category_repository,
129+
tag_repository,
126130
user_repository,
127131
user_authentication_repository,
128132
user_profile_repository,
@@ -134,6 +138,7 @@ pub async fn run(configuration: Configuration, api_implementation: &Implementati
134138
torrent_listing_generator,
135139
banned_user_list,
136140
category_service,
141+
tag_service,
137142
proxy_service,
138143
settings_service,
139144
torrent_index,

src/common.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::config::Configuration;
66
use crate::databases::database::Database;
77
use crate::services::authentication::{DbUserAuthenticationRepository, JsonWebToken, Service};
88
use crate::services::category::{self, DbCategoryRepository};
9+
use crate::services::tag::{self, DbTagRepository};
910
use crate::services::torrent::{
1011
DbTorrentAnnounceUrlRepository, DbTorrentFileRepository, DbTorrentInfoRepository, DbTorrentListingGenerator,
1112
DbTorrentRepository, DbTorrentTagRepository,
@@ -30,6 +31,7 @@ pub struct AppData {
3031
pub image_cache_manager: Arc<ImageCacheService>,
3132
// Repositories
3233
pub category_repository: Arc<DbCategoryRepository>,
34+
pub tag_repository: Arc<DbTagRepository>,
3335
pub user_repository: Arc<DbUserRepository>,
3436
pub user_authentication_repository: Arc<DbUserAuthenticationRepository>,
3537
pub user_profile_repository: Arc<DbUserProfileRepository>,
@@ -42,6 +44,7 @@ pub struct AppData {
4244
pub banned_user_list: Arc<DbBannedUserList>,
4345
// Services
4446
pub category_service: Arc<category::Service>,
47+
pub tag_service: Arc<tag::Service>,
4548
pub proxy_service: Arc<proxy::Service>,
4649
pub settings_service: Arc<settings::Service>,
4750
pub torrent_service: Arc<torrent::Index>,
@@ -63,6 +66,7 @@ impl AppData {
6366
image_cache_manager: Arc<ImageCacheService>,
6467
// Repositories
6568
category_repository: Arc<DbCategoryRepository>,
69+
tag_repository: Arc<DbTagRepository>,
6670
user_repository: Arc<DbUserRepository>,
6771
user_authentication_repository: Arc<DbUserAuthenticationRepository>,
6872
user_profile_repository: Arc<DbUserProfileRepository>,
@@ -75,6 +79,7 @@ impl AppData {
7579
banned_user_list: Arc<DbBannedUserList>,
7680
// Services
7781
category_service: Arc<category::Service>,
82+
tag_service: Arc<tag::Service>,
7883
proxy_service: Arc<proxy::Service>,
7984
settings_service: Arc<settings::Service>,
8085
torrent_service: Arc<torrent::Index>,
@@ -93,6 +98,7 @@ impl AppData {
9398
image_cache_manager,
9499
// Repositories
95100
category_repository,
101+
tag_repository,
96102
user_repository,
97103
user_authentication_repository,
98104
user_profile_repository,
@@ -105,6 +111,7 @@ impl AppData {
105111
banned_user_list,
106112
// Services
107113
category_service,
114+
tag_service,
108115
proxy_service,
109116
settings_service,
110117
torrent_service,

src/databases/database.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ pub trait Database: Sync + Send {
243243
async fn add_torrent_tag_link(&self, torrent_id: i64, tag_id: TagId) -> Result<(), Error>;
244244

245245
/// Add multiple tags to a torrent at once.
246-
async fn add_torrent_tag_links(&self, torrent_id: i64, tag_ids: &Vec<TagId>) -> Result<(), Error>;
246+
async fn add_torrent_tag_links(&self, torrent_id: i64, tag_ids: &[TagId]) -> Result<(), Error>;
247247

248248
/// Remove a tag from torrent.
249249
async fn delete_torrent_tag_link(&self, torrent_id: i64, tag_id: TagId) -> Result<(), Error>;

src/databases/mysql.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ impl Database for Mysql {
732732
.map_err(|_| database::Error::Error)
733733
}
734734

735-
async fn add_torrent_tag_links(&self, torrent_id: i64, tag_ids: &Vec<TagId>) -> Result<(), database::Error> {
735+
async fn add_torrent_tag_links(&self, torrent_id: i64, tag_ids: &[TagId]) -> Result<(), database::Error> {
736736
let mut transaction = self
737737
.pool
738738
.begin()

src/databases/sqlite.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ impl Database for Sqlite {
722722
.map_err(|_| database::Error::Error)
723723
}
724724

725-
async fn add_torrent_tag_links(&self, torrent_id: i64, tag_ids: &Vec<TagId>) -> Result<(), database::Error> {
725+
async fn add_torrent_tag_links(&self, torrent_id: i64, tag_ids: &[TagId]) -> Result<(), database::Error> {
726726
let mut transaction = self
727727
.pool
728728
.begin()

src/errors.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,17 @@ pub enum ServiceError {
124124
FailedToSendVerificationEmail,
125125

126126
#[display(fmt = "Category already exists.")]
127-
CategoryExists,
127+
CategoryAlreadyExists,
128128

129129
#[display(fmt = "Tag already exists.")]
130-
TagExists,
130+
TagAlreadyExists,
131131

132132
#[display(fmt = "Category not found.")]
133133
CategoryNotFound,
134134

135+
#[display(fmt = "Tag not found.")]
136+
TagNotFound,
137+
135138
#[display(fmt = "Database error.")]
136139
DatabaseError,
137140
}
@@ -176,14 +179,15 @@ impl ResponseError for ServiceError {
176179
ServiceError::InfoHashAlreadyExists => StatusCode::BAD_REQUEST,
177180
ServiceError::TorrentTitleAlreadyExists => StatusCode::BAD_REQUEST,
178181
ServiceError::TrackerOffline => StatusCode::INTERNAL_SERVER_ERROR,
179-
ServiceError::CategoryExists => StatusCode::BAD_REQUEST,
180-
ServiceError::TagExists => StatusCode::BAD_REQUEST,
182+
ServiceError::CategoryAlreadyExists => StatusCode::BAD_REQUEST,
183+
ServiceError::TagAlreadyExists => StatusCode::BAD_REQUEST,
181184
ServiceError::InternalServerError => StatusCode::INTERNAL_SERVER_ERROR,
182185
ServiceError::EmailMissing => StatusCode::NOT_FOUND,
183186
ServiceError::FailedToSendVerificationEmail => StatusCode::INTERNAL_SERVER_ERROR,
184187
ServiceError::WhitelistingError => StatusCode::INTERNAL_SERVER_ERROR,
185188
ServiceError::DatabaseError => StatusCode::INTERNAL_SERVER_ERROR,
186189
ServiceError::CategoryNotFound => StatusCode::NOT_FOUND,
190+
ServiceError::TagNotFound => StatusCode::NOT_FOUND,
187191
}
188192
}
189193

@@ -223,9 +227,9 @@ impl From<database::Error> for ServiceError {
223227
database::Error::UsernameTaken => ServiceError::UsernameTaken,
224228
database::Error::EmailTaken => ServiceError::EmailTaken,
225229
database::Error::UserNotFound => ServiceError::UserNotFound,
226-
database::Error::CategoryAlreadyExists => ServiceError::CategoryExists,
230+
database::Error::CategoryAlreadyExists => ServiceError::CategoryAlreadyExists,
227231
database::Error::CategoryNotFound => ServiceError::InvalidCategory,
228-
database::Error::TagAlreadyExists => ServiceError::TagExists,
232+
database::Error::TagAlreadyExists => ServiceError::TagAlreadyExists,
229233
database::Error::TagNotFound => ServiceError::InvalidTag,
230234
database::Error::TorrentNotFound => ServiceError::TorrentNotFound,
231235
database::Error::TorrentAlreadyExists => ServiceError::InfoHashAlreadyExists,

src/routes/tag.rs

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use actix_web::{web, HttpRequest, HttpResponse, Responder};
22
use serde::{Deserialize, Serialize};
33

44
use crate::common::WebAppData;
5-
use crate::errors::{ServiceError, ServiceResult};
5+
use crate::errors::ServiceResult;
66
use crate::models::response::OkResponse;
77
use crate::models::torrent_tag::TagId;
88
use crate::routes::API_VERSION;
@@ -24,7 +24,7 @@ pub fn init(cfg: &mut web::ServiceConfig) {
2424
///
2525
/// This function will return an error if unable to get tags from database.
2626
pub async fn get_all(app_data: WebAppData) -> ServiceResult<impl Responder> {
27-
let tags = app_data.torrent_tag_repository.get_tags().await?;
27+
let tags = app_data.tag_repository.get_all().await?;
2828

2929
Ok(HttpResponse::Ok().json(OkResponse { data: tags }))
3030
}
@@ -46,14 +46,7 @@ pub struct Create {
4646
pub async fn create(req: HttpRequest, payload: web::Json<Create>, app_data: WebAppData) -> ServiceResult<impl Responder> {
4747
let user_id = app_data.auth.get_user_id_from_request(&req).await?;
4848

49-
let user = app_data.user_repository.get_compact(&user_id).await?;
50-
51-
// check if user is administrator
52-
if !user.administrator {
53-
return Err(ServiceError::Unauthorized);
54-
}
55-
56-
app_data.torrent_tag_repository.add_tag(&payload.name).await?;
49+
app_data.tag_service.add_tag(&payload.name, &user_id).await?;
5750

5851
Ok(HttpResponse::Ok().json(OkResponse {
5952
data: payload.name.to_string(),
@@ -77,14 +70,7 @@ pub struct Delete {
7770
pub async fn delete(req: HttpRequest, payload: web::Json<Delete>, app_data: WebAppData) -> ServiceResult<impl Responder> {
7871
let user_id = app_data.auth.get_user_id_from_request(&req).await?;
7972

80-
let user = app_data.user_repository.get_compact(&user_id).await?;
81-
82-
// check if user is administrator
83-
if !user.administrator {
84-
return Err(ServiceError::Unauthorized);
85-
}
86-
87-
app_data.torrent_tag_repository.delete_tag(&payload.tag_id).await?;
73+
app_data.tag_service.delete_tag(&payload.tag_id, &user_id).await?;
8874

8975
Ok(HttpResponse::Ok().json(OkResponse { data: payload.tag_id }))
9076
}

src/services/category.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ impl Service {
4141
match self.category_repository.add(category_name).await {
4242
Ok(id) => Ok(id),
4343
Err(e) => match e {
44-
DatabaseError::CategoryAlreadyExists => Err(ServiceError::CategoryExists),
44+
DatabaseError::CategoryAlreadyExists => Err(ServiceError::CategoryAlreadyExists),
4545
_ => Err(ServiceError::DatabaseError),
4646
},
4747
}
4848
}
4949

50-
/// Deletes a new category.
50+
/// Deletes a category.
5151
///
5252
/// # Errors
5353
///

src/services/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ pub mod authentication;
44
pub mod category;
55
pub mod proxy;
66
pub mod settings;
7+
pub mod tag;
78
pub mod torrent;
89
pub mod user;

src/services/tag.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//! Tag service.
2+
use std::sync::Arc;
3+
4+
use super::user::DbUserRepository;
5+
use crate::databases::database::{Database, Error as DatabaseError, Error};
6+
use crate::errors::ServiceError;
7+
use crate::models::torrent_tag::{TagId, TorrentTag};
8+
use crate::models::user::UserId;
9+
10+
pub struct Service {
11+
tag_repository: Arc<DbTagRepository>,
12+
user_repository: Arc<DbUserRepository>,
13+
}
14+
15+
impl Service {
16+
#[must_use]
17+
pub fn new(tag_repository: Arc<DbTagRepository>, user_repository: Arc<DbUserRepository>) -> Service {
18+
Service {
19+
tag_repository,
20+
user_repository,
21+
}
22+
}
23+
24+
/// Adds a new tag.
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_tag(&self, tag_name: &str, user_id: &UserId) -> Result<(), 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.tag_repository.add(tag_name).await {
42+
Ok(_) => Ok(()),
43+
Err(e) => match e {
44+
DatabaseError::TagAlreadyExists => Err(ServiceError::TagAlreadyExists),
45+
_ => Err(ServiceError::DatabaseError),
46+
},
47+
}
48+
}
49+
50+
/// Deletes a tag.
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_tag(&self, tag_id: &TagId, 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.tag_repository.delete(tag_id).await {
68+
Ok(_) => Ok(()),
69+
Err(e) => match e {
70+
DatabaseError::TagNotFound => Err(ServiceError::TagNotFound),
71+
_ => Err(ServiceError::DatabaseError),
72+
},
73+
}
74+
}
75+
}
76+
77+
pub struct DbTagRepository {
78+
database: Arc<Box<dyn Database>>,
79+
}
80+
81+
impl DbTagRepository {
82+
#[must_use]
83+
pub fn new(database: Arc<Box<dyn Database>>) -> Self {
84+
Self { database }
85+
}
86+
87+
/// It adds a new tag and returns the newly created tag.
88+
///
89+
/// # Errors
90+
///
91+
/// It returns an error if there is a database error.
92+
pub async fn add(&self, tag_name: &str) -> Result<(), Error> {
93+
self.database.add_tag(tag_name).await
94+
}
95+
96+
/// It returns all the tags.
97+
///
98+
/// # Errors
99+
///
100+
/// It returns an error if there is a database error.
101+
pub async fn get_all(&self) -> Result<Vec<TorrentTag>, Error> {
102+
self.database.get_tags().await
103+
}
104+
105+
/// It removes a tag and returns it.
106+
///
107+
/// # Errors
108+
///
109+
/// It returns an error if there is a database error.
110+
pub async fn delete(&self, tag_id: &TagId) -> Result<(), Error> {
111+
self.database.delete_tag(*tag_id).await
112+
}
113+
}

0 commit comments

Comments
 (0)