feat(domain): ports, events, test helpers
This commit is contained in:
@@ -1 +1,138 @@
|
||||
// filled in Task 4
|
||||
use async_trait::async_trait;
|
||||
use crate::{
|
||||
errors::DomainError,
|
||||
events::{DomainEvent, EventEnvelope},
|
||||
models::{
|
||||
api_key::ApiKey,
|
||||
feed::{FeedEntry, PageParams, Paginated, UserSummary},
|
||||
notification::Notification,
|
||||
remote_actor::RemoteActor,
|
||||
social::{Block, Boost, Follow, FollowState, Like},
|
||||
tag::Tag,
|
||||
thought::Thought,
|
||||
top_friend::TopFriend,
|
||||
user::User,
|
||||
},
|
||||
value_objects::{ApiKeyId, Content, Email, NotificationId, PasswordHash, ThoughtId, UserId, Username},
|
||||
};
|
||||
|
||||
pub struct GeneratedToken { pub token: String, pub user_id: UserId }
|
||||
|
||||
#[async_trait]
|
||||
pub trait AuthService: Send + Sync {
|
||||
fn generate_token(&self, user_id: &UserId) -> Result<GeneratedToken, DomainError>;
|
||||
fn validate_token(&self, token: &str) -> Result<UserId, DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait PasswordHasher: Send + Sync {
|
||||
async fn hash(&self, plain: &str) -> Result<PasswordHash, DomainError>;
|
||||
async fn verify(&self, plain: &str, hash: &PasswordHash) -> Result<bool, DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait EventPublisher: Send + Sync {
|
||||
async fn publish(&self, event: &DomainEvent) -> Result<(), DomainError>;
|
||||
}
|
||||
|
||||
pub trait EventConsumer: Send + Sync {
|
||||
fn consume(&self) -> futures::stream::BoxStream<'_, Result<EventEnvelope, DomainError>>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait UserRepository: Send + Sync {
|
||||
async fn find_by_id(&self, id: &UserId) -> Result<Option<User>, DomainError>;
|
||||
async fn find_by_username(&self, username: &Username) -> Result<Option<User>, DomainError>;
|
||||
async fn find_by_email(&self, email: &Email) -> Result<Option<User>, DomainError>;
|
||||
async fn save(&self, user: &User) -> Result<(), DomainError>;
|
||||
async fn update_profile(&self, user_id: &UserId, display_name: Option<String>, bio: Option<String>, avatar_url: Option<String>, header_url: Option<String>, custom_css: Option<String>) -> Result<(), DomainError>;
|
||||
async fn list_with_stats(&self) -> Result<Vec<UserSummary>, DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait ThoughtRepository: Send + Sync {
|
||||
async fn save(&self, thought: &Thought) -> Result<(), DomainError>;
|
||||
async fn find_by_id(&self, id: &ThoughtId) -> Result<Option<Thought>, DomainError>;
|
||||
async fn delete(&self, id: &ThoughtId, user_id: &UserId) -> Result<(), DomainError>;
|
||||
async fn update_content(&self, id: &ThoughtId, content: &Content) -> Result<(), DomainError>;
|
||||
async fn get_thread(&self, id: &ThoughtId) -> Result<Vec<Thought>, DomainError>;
|
||||
async fn list_by_user(&self, user_id: &UserId, page: &PageParams) -> Result<Paginated<FeedEntry>, DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait LikeRepository: Send + Sync {
|
||||
async fn save(&self, like: &Like) -> Result<(), DomainError>;
|
||||
async fn delete(&self, user_id: &UserId, thought_id: &ThoughtId) -> Result<(), DomainError>;
|
||||
async fn find(&self, user_id: &UserId, thought_id: &ThoughtId) -> Result<Option<Like>, DomainError>;
|
||||
async fn count_for_thought(&self, thought_id: &ThoughtId) -> Result<i64, DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait BoostRepository: Send + Sync {
|
||||
async fn save(&self, boost: &Boost) -> Result<(), DomainError>;
|
||||
async fn delete(&self, user_id: &UserId, thought_id: &ThoughtId) -> Result<(), DomainError>;
|
||||
async fn find(&self, user_id: &UserId, thought_id: &ThoughtId) -> Result<Option<Boost>, DomainError>;
|
||||
async fn count_for_thought(&self, thought_id: &ThoughtId) -> Result<i64, DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait FollowRepository: Send + Sync {
|
||||
async fn save(&self, follow: &Follow) -> Result<(), DomainError>;
|
||||
async fn delete(&self, follower_id: &UserId, following_id: &UserId) -> Result<(), DomainError>;
|
||||
async fn find(&self, follower_id: &UserId, following_id: &UserId) -> Result<Option<Follow>, DomainError>;
|
||||
async fn update_state(&self, follower_id: &UserId, following_id: &UserId, state: &FollowState) -> Result<(), DomainError>;
|
||||
async fn list_followers(&self, user_id: &UserId, page: &PageParams) -> Result<Paginated<User>, DomainError>;
|
||||
async fn list_following(&self, user_id: &UserId, page: &PageParams) -> Result<Paginated<User>, DomainError>;
|
||||
async fn get_accepted_following_ids(&self, user_id: &UserId) -> Result<Vec<UserId>, DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait BlockRepository: Send + Sync {
|
||||
async fn save(&self, block: &Block) -> Result<(), DomainError>;
|
||||
async fn delete(&self, blocker_id: &UserId, blocked_id: &UserId) -> Result<(), DomainError>;
|
||||
async fn exists(&self, blocker_id: &UserId, blocked_id: &UserId) -> Result<bool, DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait TagRepository: Send + Sync {
|
||||
async fn find_or_create(&self, name: &str) -> Result<Tag, DomainError>;
|
||||
async fn attach_to_thought(&self, thought_id: &ThoughtId, tag_id: i32) -> Result<(), DomainError>;
|
||||
async fn detach_from_thought(&self, thought_id: &ThoughtId) -> Result<(), DomainError>;
|
||||
async fn list_for_thought(&self, thought_id: &ThoughtId) -> Result<Vec<Tag>, DomainError>;
|
||||
async fn list_thoughts_by_tag(&self, tag_name: &str, page: &PageParams) -> Result<Paginated<Thought>, DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait ApiKeyRepository: Send + Sync {
|
||||
async fn save(&self, key: &ApiKey) -> Result<(), DomainError>;
|
||||
async fn find_by_hash(&self, key_hash: &str) -> Result<Option<ApiKey>, DomainError>;
|
||||
async fn list_for_user(&self, user_id: &UserId) -> Result<Vec<ApiKey>, DomainError>;
|
||||
async fn delete(&self, id: &ApiKeyId, user_id: &UserId) -> Result<(), DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait TopFriendRepository: Send + Sync {
|
||||
async fn set_top_friends(&self, user_id: &UserId, friends: Vec<(UserId, i16)>) -> Result<(), DomainError>;
|
||||
async fn list_for_user(&self, user_id: &UserId) -> Result<Vec<(TopFriend, User)>, DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait NotificationRepository: Send + Sync {
|
||||
async fn save(&self, n: &Notification) -> Result<(), DomainError>;
|
||||
async fn list_for_user(&self, user_id: &UserId, page: &PageParams) -> Result<Paginated<Notification>, DomainError>;
|
||||
async fn mark_read(&self, id: &NotificationId, user_id: &UserId) -> Result<(), DomainError>;
|
||||
async fn mark_all_read(&self, user_id: &UserId) -> Result<(), DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait RemoteActorRepository: Send + Sync {
|
||||
async fn upsert(&self, actor: &RemoteActor) -> Result<(), DomainError>;
|
||||
async fn find_by_url(&self, url: &str) -> Result<Option<RemoteActor>, DomainError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait FeedRepository: Send + Sync {
|
||||
async fn home_feed(&self, following_ids: &[UserId], page: &PageParams, viewer_id: Option<&UserId>) -> Result<Paginated<FeedEntry>, DomainError>;
|
||||
async fn public_feed(&self, page: &PageParams, viewer_id: Option<&UserId>) -> Result<Paginated<FeedEntry>, DomainError>;
|
||||
async fn search(&self, query: &str, page: &PageParams, viewer_id: Option<&UserId>) -> Result<Paginated<FeedEntry>, DomainError>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user