From 9759a0cbc5ff8b785d1e941b15abc6b507b0732f Mon Sep 17 00:00:00 2001 From: Mario Date: Fri, 24 Jan 2025 17:00:22 +0100 Subject: [PATCH] feat: [#799] get user profiles endpoint now supports searching --- src/databases/database.rs | 7 ++++++- src/databases/mysql.rs | 16 ++++++++++++++-- src/databases/sqlite.rs | 16 ++++++++++++++-- src/services/authorization.rs | 1 + src/services/user.rs | 10 ++++++++-- 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/databases/database.rs b/src/databases/database.rs index 7d41758d..ba5c720f 100644 --- a/src/databases/database.rs +++ b/src/databases/database.rs @@ -144,7 +144,12 @@ pub trait Database: Sync + Send { async fn get_user_profile_from_username(&self, username: &str) -> Result; /// Get all user profiles in a paginated form as `UserProfilesResponse`. - async fn get_user_profiles_paginated(&self, offset: u64, page_size: u8) -> Result; + async fn get_user_profiles_search_paginated( + &self, + search: &Option, + offset: u64, + page_size: u8, + ) -> Result; /// Get `UserCompact` from `user_id`. async fn get_user_compact_from_id(&self, user_id: i64) -> Result; diff --git a/src/databases/mysql.rs b/src/databases/mysql.rs index 99eaacbe..a4b93210 100644 --- a/src/databases/mysql.rs +++ b/src/databases/mysql.rs @@ -155,12 +155,23 @@ impl Database for Mysql { .map_err(|_| database::Error::UserNotFound) } - async fn get_user_profiles_paginated(&self, offset: u64, limit: u8) -> Result { - let mut query_string = "SELECT * FROM torrust_user_profiles".to_string(); + async fn get_user_profiles_search_paginated( + &self, + search: &Option, + offset: u64, + limit: u8, + ) -> Result { + let user_name = match search { + None => "%".to_string(), + Some(v) => format!("%{v}%"), + }; + + let mut query_string = "SELECT * FROM torrust_user_profiles WHERE username LIKE ?".to_string(); let count_query = format!("SELECT COUNT(*) as count FROM ({query_string}) AS count_table"); let count_result: Result = query_as(&count_query) + .bind(user_name.clone()) .fetch_one(&self.pool) .await .map(|(v,)| v) @@ -171,6 +182,7 @@ impl Database for Mysql { query_string = format!("{query_string} LIMIT ?, ?"); let res: Vec = sqlx::query_as::<_, UserProfile>(&query_string) + .bind(user_name.clone()) .bind(i64::saturating_add_unsigned(0, offset)) .bind(limit) .fetch_all(&self.pool) diff --git a/src/databases/sqlite.rs b/src/databases/sqlite.rs index 337a5858..a3e77774 100644 --- a/src/databases/sqlite.rs +++ b/src/databases/sqlite.rs @@ -156,12 +156,23 @@ impl Database for Sqlite { .map_err(|_| database::Error::UserNotFound) } - async fn get_user_profiles_paginated(&self, offset: u64, limit: u8) -> Result { - let mut query_string = "SELECT * FROM torrust_user_profiles".to_string(); + async fn get_user_profiles_search_paginated( + &self, + search: &Option, + offset: u64, + limit: u8, + ) -> Result { + let user_name = match search { + None => "%".to_string(), + Some(v) => format!("%{v}%"), + }; + + let mut query_string = "SELECT * FROM torrust_user_profiles WHERE username LIKE ?".to_string(); let count_query = format!("SELECT COUNT(*) as count FROM ({query_string}) AS count_table"); let count_result: Result = query_as(&count_query) + .bind(user_name.clone()) .fetch_one(&self.pool) .await .map(|(v,)| v) @@ -172,6 +183,7 @@ impl Database for Sqlite { query_string = format!("{query_string} LIMIT ?, ?"); let res: Vec = sqlx::query_as::<_, UserProfile>(&query_string) + .bind(user_name.clone()) .bind(i64::saturating_add_unsigned(0, offset)) .bind(limit) .fetch_all(&self.pool) diff --git a/src/services/authorization.rs b/src/services/authorization.rs index df3d30c8..9aa11a8e 100644 --- a/src/services/authorization.rs +++ b/src/services/authorization.rs @@ -273,6 +273,7 @@ impl Default for CasbinConfiguration { guest, GetTorrentInfo guest, GenerateTorrentInfoListing guest, GetCanonicalInfoHash + guest, GenerateUserProfilesListing ", ), } diff --git a/src/services/user.rs b/src/services/user.rs index 083a0e2c..c55443b1 100644 --- a/src/services/user.rs +++ b/src/services/user.rs @@ -36,6 +36,7 @@ fn no_email() -> String { pub struct ListingRequest { pub page_size: Option, pub page: Option, + pub search: Option, } /// Internal specification for user profiles listings. @@ -43,6 +44,7 @@ pub struct ListingRequest { pub struct ListingSpecification { pub offset: u64, pub page_size: u8, + pub search: Option, } pub struct RegistrationService { @@ -401,7 +403,11 @@ impl ListingService { let offset = u64::from(page * u32::from(page_size)); - ListingSpecification { offset, page_size } + ListingSpecification { + search: request.search.clone(), + offset, + page_size, + } } } @@ -504,7 +510,7 @@ impl DbUserProfileRepository { /// It returns an error if there is a database error. pub async fn generate_listing(&self, specification: &ListingSpecification) -> Result { self.database - .get_user_profiles_paginated(specification.offset, specification.page_size) + .get_user_profiles_search_paginated(&specification.search, specification.offset, specification.page_size) .await } }