Refactor handlers and OpenAPI documentation for improved readability and consistency
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 6m49s
test / unit (pull_request) Successful in 16m24s
test / integration (pull_request) Failing after 17m7s

- Reorganized imports in health, notifications, social, thoughts, and users handlers for clarity.
- Updated function signatures in handlers to improve readability by aligning parameters.
- Enhanced JSON response formatting in notifications and thoughts handlers.
- Improved error handling in user-related functions.
- Refactored OpenAPI documentation to maintain consistent formatting and structure.
- Cleaned up unnecessary code and comments across various files.
- Ensured consistent use of `Arc` for shared state in AppState and WorkerHandlers.
This commit is contained in:
2026-05-14 16:28:57 +02:00
parent 004bfb427b
commit 10c4a66de5
47 changed files with 2406 additions and 723 deletions

View File

@@ -7,63 +7,185 @@ use domain::{
value_objects::{BoostId, LikeId, ThoughtId, UserId},
};
pub async fn like_thought(likes: &dyn LikeRepository, events: &dyn EventPublisher, user_id: &UserId, thought_id: &ThoughtId) -> Result<(), DomainError> {
let like = Like { id: LikeId::new(), user_id: user_id.clone(), thought_id: thought_id.clone(), ap_id: None, created_at: Utc::now() };
pub async fn like_thought(
likes: &dyn LikeRepository,
events: &dyn EventPublisher,
user_id: &UserId,
thought_id: &ThoughtId,
) -> Result<(), DomainError> {
let like = Like {
id: LikeId::new(),
user_id: user_id.clone(),
thought_id: thought_id.clone(),
ap_id: None,
created_at: Utc::now(),
};
likes.save(&like).await?;
events.publish(&DomainEvent::LikeAdded { like_id: like.id, user_id: user_id.clone(), thought_id: thought_id.clone() }).await?;
events
.publish(&DomainEvent::LikeAdded {
like_id: like.id,
user_id: user_id.clone(),
thought_id: thought_id.clone(),
})
.await?;
Ok(())
}
pub async fn unlike_thought(likes: &dyn LikeRepository, events: &dyn EventPublisher, user_id: &UserId, thought_id: &ThoughtId) -> Result<(), DomainError> {
pub async fn unlike_thought(
likes: &dyn LikeRepository,
events: &dyn EventPublisher,
user_id: &UserId,
thought_id: &ThoughtId,
) -> Result<(), DomainError> {
likes.delete(user_id, thought_id).await?;
events.publish(&DomainEvent::LikeRemoved { user_id: user_id.clone(), thought_id: thought_id.clone() }).await?;
events
.publish(&DomainEvent::LikeRemoved {
user_id: user_id.clone(),
thought_id: thought_id.clone(),
})
.await?;
Ok(())
}
pub async fn boost_thought(boosts: &dyn BoostRepository, events: &dyn EventPublisher, user_id: &UserId, thought_id: &ThoughtId) -> Result<(), DomainError> {
let boost = Boost { id: BoostId::new(), user_id: user_id.clone(), thought_id: thought_id.clone(), ap_id: None, created_at: Utc::now() };
pub async fn boost_thought(
boosts: &dyn BoostRepository,
events: &dyn EventPublisher,
user_id: &UserId,
thought_id: &ThoughtId,
) -> Result<(), DomainError> {
let boost = Boost {
id: BoostId::new(),
user_id: user_id.clone(),
thought_id: thought_id.clone(),
ap_id: None,
created_at: Utc::now(),
};
boosts.save(&boost).await?;
events.publish(&DomainEvent::BoostAdded { boost_id: boost.id, user_id: user_id.clone(), thought_id: thought_id.clone() }).await?;
events
.publish(&DomainEvent::BoostAdded {
boost_id: boost.id,
user_id: user_id.clone(),
thought_id: thought_id.clone(),
})
.await?;
Ok(())
}
pub async fn unboost_thought(boosts: &dyn BoostRepository, events: &dyn EventPublisher, user_id: &UserId, thought_id: &ThoughtId) -> Result<(), DomainError> {
pub async fn unboost_thought(
boosts: &dyn BoostRepository,
events: &dyn EventPublisher,
user_id: &UserId,
thought_id: &ThoughtId,
) -> Result<(), DomainError> {
boosts.delete(user_id, thought_id).await?;
events.publish(&DomainEvent::BoostRemoved { user_id: user_id.clone(), thought_id: thought_id.clone() }).await?;
events
.publish(&DomainEvent::BoostRemoved {
user_id: user_id.clone(),
thought_id: thought_id.clone(),
})
.await?;
Ok(())
}
pub async fn follow_user(follows: &dyn FollowRepository, events: &dyn EventPublisher, follower_id: &UserId, following_id: &UserId) -> Result<(), DomainError> {
if follower_id == following_id { return Err(DomainError::InvalidInput("cannot follow yourself".into())); }
let follow = Follow { follower_id: follower_id.clone(), following_id: following_id.clone(), state: FollowState::Accepted, ap_id: None, created_at: Utc::now() };
pub async fn follow_user(
follows: &dyn FollowRepository,
events: &dyn EventPublisher,
follower_id: &UserId,
following_id: &UserId,
) -> Result<(), DomainError> {
if follower_id == following_id {
return Err(DomainError::InvalidInput("cannot follow yourself".into()));
}
let follow = Follow {
follower_id: follower_id.clone(),
following_id: following_id.clone(),
state: FollowState::Accepted,
ap_id: None,
created_at: Utc::now(),
};
follows.save(&follow).await?;
events.publish(&DomainEvent::FollowAccepted { follower_id: follower_id.clone(), following_id: following_id.clone() }).await?;
events
.publish(&DomainEvent::FollowAccepted {
follower_id: follower_id.clone(),
following_id: following_id.clone(),
})
.await?;
Ok(())
}
pub async fn unfollow_user(follows: &dyn FollowRepository, events: &dyn EventPublisher, follower_id: &UserId, following_id: &UserId) -> Result<(), DomainError> {
pub async fn unfollow_user(
follows: &dyn FollowRepository,
events: &dyn EventPublisher,
follower_id: &UserId,
following_id: &UserId,
) -> Result<(), DomainError> {
follows.delete(follower_id, following_id).await?;
events.publish(&DomainEvent::Unfollowed { follower_id: follower_id.clone(), following_id: following_id.clone() }).await?;
events
.publish(&DomainEvent::Unfollowed {
follower_id: follower_id.clone(),
following_id: following_id.clone(),
})
.await?;
Ok(())
}
pub async fn accept_follow(follows: &dyn FollowRepository, events: &dyn EventPublisher, follower_id: &UserId, following_id: &UserId) -> Result<(), DomainError> {
follows.update_state(follower_id, following_id, &FollowState::Accepted).await?;
events.publish(&DomainEvent::FollowAccepted { follower_id: follower_id.clone(), following_id: following_id.clone() }).await?;
pub async fn accept_follow(
follows: &dyn FollowRepository,
events: &dyn EventPublisher,
follower_id: &UserId,
following_id: &UserId,
) -> Result<(), DomainError> {
follows
.update_state(follower_id, following_id, &FollowState::Accepted)
.await?;
events
.publish(&DomainEvent::FollowAccepted {
follower_id: follower_id.clone(),
following_id: following_id.clone(),
})
.await?;
Ok(())
}
pub async fn reject_follow(follows: &dyn FollowRepository, events: &dyn EventPublisher, follower_id: &UserId, following_id: &UserId) -> Result<(), DomainError> {
follows.update_state(follower_id, following_id, &FollowState::Rejected).await?;
events.publish(&DomainEvent::FollowRejected { follower_id: follower_id.clone(), following_id: following_id.clone() }).await?;
pub async fn reject_follow(
follows: &dyn FollowRepository,
events: &dyn EventPublisher,
follower_id: &UserId,
following_id: &UserId,
) -> Result<(), DomainError> {
follows
.update_state(follower_id, following_id, &FollowState::Rejected)
.await?;
events
.publish(&DomainEvent::FollowRejected {
follower_id: follower_id.clone(),
following_id: following_id.clone(),
})
.await?;
Ok(())
}
pub async fn block_user(blocks: &dyn BlockRepository, events: &dyn EventPublisher, blocker_id: &UserId, blocked_id: &UserId) -> Result<(), DomainError> {
if blocker_id == blocked_id { return Err(DomainError::InvalidInput("cannot block yourself".into())); }
let block = Block { blocker_id: blocker_id.clone(), blocked_id: blocked_id.clone(), created_at: Utc::now() };
pub async fn block_user(
blocks: &dyn BlockRepository,
events: &dyn EventPublisher,
blocker_id: &UserId,
blocked_id: &UserId,
) -> Result<(), DomainError> {
if blocker_id == blocked_id {
return Err(DomainError::InvalidInput("cannot block yourself".into()));
}
let block = Block {
blocker_id: blocker_id.clone(),
blocked_id: blocked_id.clone(),
created_at: Utc::now(),
};
blocks.save(&block).await?;
events.publish(&DomainEvent::UserBlocked { blocker_id: blocker_id.clone(), blocked_id: blocked_id.clone() }).await?;
events
.publish(&DomainEvent::UserBlocked {
blocker_id: blocker_id.clone(),
blocked_id: blocked_id.clone(),
})
.await?;
Ok(())
}
@@ -74,10 +196,12 @@ pub async fn unblock_user(
blocked_id: &UserId,
) -> Result<(), DomainError> {
blocks.delete(blocker_id, blocked_id).await?;
events.publish(&DomainEvent::UserUnblocked {
blocker_id: blocker_id.clone(),
blocked_id: blocked_id.clone(),
}).await?;
events
.publish(&DomainEvent::UserUnblocked {
blocker_id: blocker_id.clone(),
blocked_id: blocked_id.clone(),
})
.await?;
Ok(())
}
@@ -85,13 +209,21 @@ pub async fn unblock_user(
mod tests {
use super::*;
use domain::{
models::{thought::{Thought, Visibility}, user::User},
models::{
thought::{Thought, Visibility},
user::User,
},
testing::TestStore,
value_objects::*,
};
fn user(name: &str) -> User {
User::new_local(UserId::new(), Username::new(name).unwrap(), Email::new(format!("{name}@ex.com")).unwrap(), PasswordHash("h".into()))
User::new_local(
UserId::new(),
Username::new(name).unwrap(),
Email::new(format!("{name}@ex.com")).unwrap(),
PasswordHash("h".into()),
)
}
#[tokio::test]
@@ -99,20 +231,35 @@ mod tests {
let store = TestStore::default();
let alice = user("alice");
let tid = ThoughtId::new();
store.thoughts.lock().unwrap().push(Thought::new_local(tid.clone(), alice.id.clone(), Content::new_local("hi").unwrap(), None, Visibility::Public, None, false));
store.thoughts.lock().unwrap().push(Thought::new_local(
tid.clone(),
alice.id.clone(),
Content::new_local("hi").unwrap(),
None,
Visibility::Public,
None,
false,
));
like_thought(&store, &store, &alice.id, &tid).await.unwrap();
assert_eq!(store.likes.lock().unwrap().len(), 1);
unlike_thought(&store, &store, &alice.id, &tid).await.unwrap();
unlike_thought(&store, &store, &alice.id, &tid)
.await
.unwrap();
assert!(store.likes.lock().unwrap().is_empty());
}
#[tokio::test]
async fn follow_and_unfollow() {
let store = TestStore::default();
let alice = user("alice"); let bob = user("bob");
follow_user(&store, &store, &alice.id, &bob.id).await.unwrap();
let alice = user("alice");
let bob = user("bob");
follow_user(&store, &store, &alice.id, &bob.id)
.await
.unwrap();
assert_eq!(store.follows.lock().unwrap().len(), 1);
unfollow_user(&store, &store, &alice.id, &bob.id).await.unwrap();
unfollow_user(&store, &store, &alice.id, &bob.id)
.await
.unwrap();
assert!(store.follows.lock().unwrap().is_empty());
}
@@ -120,7 +267,9 @@ mod tests {
async fn cannot_follow_self() {
let store = TestStore::default();
let alice = user("alice");
let err = follow_user(&store, &store, &alice.id, &alice.id).await.unwrap_err();
let err = follow_user(&store, &store, &alice.id, &alice.id)
.await
.unwrap_err();
assert!(matches!(err, DomainError::InvalidInput(_)));
}
@@ -129,9 +278,13 @@ mod tests {
let store = TestStore::default();
let alice = user("alice");
let bob = user("bob");
block_user(&store, &store, &alice.id, &bob.id).await.unwrap();
block_user(&store, &store, &alice.id, &bob.id)
.await
.unwrap();
store.events.lock().unwrap().clear();
unblock_user(&store, &store, &alice.id, &bob.id).await.unwrap();
unblock_user(&store, &store, &alice.id, &bob.id)
.await
.unwrap();
let events = store.events.lock().unwrap();
assert_eq!(events.len(), 1);
assert!(matches!(events[0], DomainEvent::UserUnblocked { .. }));
@@ -142,17 +295,23 @@ mod tests {
let store = TestStore::default();
let alice = user("alice");
let bob = user("bob");
block_user(&store, &store, &alice.id, &bob.id).await.unwrap();
block_user(&store, &store, &alice.id, &bob.id)
.await
.unwrap();
assert_eq!(store.blocks.lock().unwrap().len(), 1);
let events = store.events.lock().unwrap();
assert!(events.iter().any(|e| matches!(e, DomainEvent::UserBlocked { blocker_id, .. } if blocker_id == &alice.id)));
assert!(events.iter().any(
|e| matches!(e, DomainEvent::UserBlocked { blocker_id, .. } if blocker_id == &alice.id)
));
}
#[tokio::test]
async fn cannot_block_self() {
let store = TestStore::default();
let alice = user("alice");
let err = block_user(&store, &store, &alice.id, &alice.id).await.unwrap_err();
let err = block_user(&store, &store, &alice.id, &alice.id)
.await
.unwrap_err();
assert!(matches!(err, DomainError::InvalidInput(_)));
}
@@ -161,12 +320,20 @@ mod tests {
let store = TestStore::default();
let alice = user("alice");
let tid = ThoughtId::new();
boost_thought(&store, &store, &alice.id, &tid).await.unwrap();
boost_thought(&store, &store, &alice.id, &tid)
.await
.unwrap();
assert_eq!(store.boosts.lock().unwrap().len(), 1);
unboost_thought(&store, &store, &alice.id, &tid).await.unwrap();
unboost_thought(&store, &store, &alice.id, &tid)
.await
.unwrap();
assert!(store.boosts.lock().unwrap().is_empty());
let events = store.events.lock().unwrap();
assert!(events.iter().any(|e| matches!(e, DomainEvent::BoostAdded { .. })));
assert!(events.iter().any(|e| matches!(e, DomainEvent::BoostRemoved { .. })));
assert!(events
.iter()
.any(|e| matches!(e, DomainEvent::BoostAdded { .. })));
assert!(events
.iter()
.any(|e| matches!(e, DomainEvent::BoostRemoved { .. })));
}
}