Refactor database error handling across repositories to use IntoDbResult for improved error management
Some checks failed
lint / lint (push) Has been cancelled
test / unit (push) Has been cancelled
test / integration (push) Has been cancelled
lint / lint (pull_request) Failing after 9m30s
test / unit (pull_request) Successful in 16m10s
test / integration (pull_request) Failing after 16m44s

- Updated PgNotificationRepository to utilize IntoDbResult for error handling in various methods.
- Refactored PgRemoteActorRepository to replace manual error mapping with IntoDbResult.
- Modified PgRemoteActorConnectionRepository to implement IntoDbResult for error handling.
- Adjusted PgTagRepository to use IntoDbResult for consistent error management.
- Introduced test_helpers module for seeding users and thoughts in tests.
- Enhanced PgThoughtRepository to leverage IntoDbResult for error handling.
- Updated PgTopFriendRepository to utilize IntoDbResult for error management.
- Refactored PgUserRepository to implement IntoDbResult for error handling.
- Added constants for pagination defaults in requests.
- Introduced MAX_TOP_FRIENDS constant for top friends validation.
- Refactored JWT expiration time to use a constant.
- Improved rate limiter configuration with constants for better readability.
- Added utility methods for FollowState and Visibility enums for string conversions.
- Introduced maximum length constants for Username, Email, and Content value objects.
- Cleaned up test modules by removing redundant code and utilizing a shared testing state.
This commit is contained in:
2026-05-15 12:31:25 +02:00
parent a040a38036
commit 314dad5451
40 changed files with 456 additions and 690 deletions

View File

@@ -1,24 +1,7 @@
use crate::db_error::IntoDbResult;
use async_trait::async_trait;
use chrono::{DateTime, Utc};
fn follow_state_from_str(s: &str) -> domain::models::social::FollowState {
use domain::models::social::FollowState;
match s {
"pending" => FollowState::Pending,
"rejected" => FollowState::Rejected,
_ => FollowState::Accepted,
}
}
fn follow_state_as_str(state: &domain::models::social::FollowState) -> &'static str {
use domain::models::social::FollowState;
match state {
FollowState::Pending => "pending",
FollowState::Accepted => "accepted",
FollowState::Rejected => "rejected",
}
}
use domain::{
errors::DomainError,
models::{
@@ -50,12 +33,12 @@ impl FollowRepository for PgFollowRepository {
)
.bind(f.follower_id.as_uuid())
.bind(f.following_id.as_uuid())
.bind(follow_state_as_str(&f.state))
.bind(f.state.as_str())
.bind(&f.ap_id)
.bind(f.created_at)
.execute(&self.pool)
.await
.map_err(|e| DomainError::Internal(e.to_string()))
.into_domain()
.map(|_| ())
}
@@ -65,7 +48,7 @@ impl FollowRepository for PgFollowRepository {
.bind(following_id.as_uuid())
.execute(&self.pool)
.await
.map_err(|e| DomainError::Internal(e.to_string()))?;
.into_domain()?;
if r.rows_affected() == 0 {
return Err(DomainError::NotFound);
}
@@ -92,11 +75,11 @@ impl FollowRepository for PgFollowRepository {
.bind(following_id.as_uuid())
.fetch_optional(&self.pool)
.await
.map_err(|e| DomainError::Internal(e.to_string()))
.into_domain()
.map(|o| o.map(|r| Follow {
follower_id: UserId::from_uuid(r.follower_id),
following_id: UserId::from_uuid(r.following_id),
state: follow_state_from_str(&r.state),
state: FollowState::from_db_str(&r.state),
ap_id: r.ap_id,
created_at: r.created_at,
}))
@@ -111,10 +94,10 @@ impl FollowRepository for PgFollowRepository {
sqlx::query("UPDATE follows SET state=$3 WHERE follower_id=$1 AND following_id=$2")
.bind(follower_id.as_uuid())
.bind(following_id.as_uuid())
.bind(follow_state_as_str(state))
.bind(state.as_str())
.execute(&self.pool)
.await
.map_err(|e| DomainError::Internal(e.to_string()))
.into_domain()
.map(|_| ())
}
@@ -129,7 +112,7 @@ impl FollowRepository for PgFollowRepository {
.bind(user_id.as_uuid())
.fetch_one(&self.pool)
.await
.map_err(|e| DomainError::Internal(e.to_string()))?;
.into_domain()?;
let rows = sqlx::query_as::<_, crate::user::UserRow>(
"SELECT u.id,u.username,u.email,u.password_hash,u.display_name,u.bio,u.avatar_url,u.header_url,u.custom_css,u.local,u.ap_id,u.inbox_url,u.created_at,u.updated_at
@@ -142,7 +125,7 @@ impl FollowRepository for PgFollowRepository {
.bind(page.offset())
.fetch_all(&self.pool)
.await
.map_err(|e| DomainError::Internal(e.to_string()))?;
.into_domain()?;
Ok(Paginated {
items: rows.into_iter().map(User::from).collect(),
@@ -163,7 +146,7 @@ impl FollowRepository for PgFollowRepository {
.bind(user_id.as_uuid())
.fetch_one(&self.pool)
.await
.map_err(|e| DomainError::Internal(e.to_string()))?;
.into_domain()?;
let rows = sqlx::query_as::<_, crate::user::UserRow>(
"SELECT u.id,u.username,u.email,u.password_hash,u.display_name,u.bio,u.avatar_url,u.header_url,u.custom_css,u.local,u.ap_id,u.inbox_url,u.created_at,u.updated_at
@@ -176,7 +159,7 @@ impl FollowRepository for PgFollowRepository {
.bind(page.offset())
.fetch_all(&self.pool)
.await
.map_err(|e| DomainError::Internal(e.to_string()))?;
.into_domain()?;
Ok(Paginated {
items: rows.into_iter().map(User::from).collect(),
@@ -196,7 +179,7 @@ impl FollowRepository for PgFollowRepository {
.bind(user_id.as_uuid())
.fetch_all(&self.pool)
.await
.map_err(|e| DomainError::Internal(e.to_string()))?;
.into_domain()?;
Ok(ids.into_iter().map(UserId::from_uuid).collect())
}
}
@@ -204,22 +187,9 @@ impl FollowRepository for PgFollowRepository {
#[cfg(test)]
mod tests {
use super::*;
use crate::user::PgUserRepository;
use crate::test_helpers::seed_user;
use chrono::Utc;
use domain::ports::UserRepository;
use domain::{models::user::User, value_objects::*};
async fn seed_user(pool: &sqlx::PgPool, username: &str, email: &str) -> User {
let repo = PgUserRepository::new(pool.clone());
let u = User::new_local(
UserId::new(),
Username::new(username).unwrap(),
Email::new(email).unwrap(),
PasswordHash("h".into()),
);
repo.save(&u).await.unwrap();
u
}
use domain::value_objects::*;
#[sqlx::test(migrations = "./migrations")]
async fn save_and_find_follow(pool: sqlx::PgPool) {