refactor: LOW+MEDIUM cleanups — CQRS DiaryQuery/GoalCommand+GoalQuery,
test-helpers out of production, decompose get_user_profile_html, dedup secure_flag, remove vestigial social_query, drop PersistedImportSession, LoginCommand rename, DeleteAccountDeps, PersonId macro, ReviewSortBy rename, TUI Command for fs::read, generic PaginatedResponse<T>, noop ports for production fallbacks
This commit is contained in:
@@ -5,7 +5,7 @@ use domain::{
|
|||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
events::DomainEvent,
|
events::DomainEvent,
|
||||||
ports::{
|
ports::{
|
||||||
GoalRepository, LocalApContentQuery, MovieQuery, ReviewRepository, StatsRepository,
|
GoalQuery, LocalApContentQuery, MovieQuery, ReviewRepository, StatsRepository,
|
||||||
UserFederationSettingsQuery,
|
UserFederationSettingsQuery,
|
||||||
},
|
},
|
||||||
value_objects::{MovieId, ReviewId, UserId},
|
value_objects::{MovieId, ReviewId, UserId},
|
||||||
@@ -22,7 +22,7 @@ pub struct ActivityPubEventHandler {
|
|||||||
content_query: Arc<dyn LocalApContentQuery>,
|
content_query: Arc<dyn LocalApContentQuery>,
|
||||||
review_repo: Arc<dyn ReviewRepository>,
|
review_repo: Arc<dyn ReviewRepository>,
|
||||||
movie_repo: Arc<dyn MovieQuery>,
|
movie_repo: Arc<dyn MovieQuery>,
|
||||||
goal_repo: Arc<dyn GoalRepository>,
|
goal_repo: Arc<dyn GoalQuery>,
|
||||||
stats_repo: Arc<dyn StatsRepository>,
|
stats_repo: Arc<dyn StatsRepository>,
|
||||||
federation_settings: Arc<dyn UserFederationSettingsQuery>,
|
federation_settings: Arc<dyn UserFederationSettingsQuery>,
|
||||||
base_url: String,
|
base_url: String,
|
||||||
@@ -35,7 +35,7 @@ impl ActivityPubEventHandler {
|
|||||||
content_query: Arc<dyn LocalApContentQuery>,
|
content_query: Arc<dyn LocalApContentQuery>,
|
||||||
review_repo: Arc<dyn ReviewRepository>,
|
review_repo: Arc<dyn ReviewRepository>,
|
||||||
movie_repo: Arc<dyn MovieQuery>,
|
movie_repo: Arc<dyn MovieQuery>,
|
||||||
goal_repo: Arc<dyn GoalRepository>,
|
goal_repo: Arc<dyn GoalQuery>,
|
||||||
stats_repo: Arc<dyn StatsRepository>,
|
stats_repo: Arc<dyn StatsRepository>,
|
||||||
federation_settings: Arc<dyn UserFederationSettingsQuery>,
|
federation_settings: Arc<dyn UserFederationSettingsQuery>,
|
||||||
base_url: String,
|
base_url: String,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use async_trait::async_trait;
|
|||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use domain::{
|
use domain::{
|
||||||
models::RemoteGoalEntry,
|
models::RemoteGoalEntry,
|
||||||
ports::{GoalRepository, RemoteGoalRepository},
|
ports::{GoalQuery, RemoteGoalRepository},
|
||||||
value_objects::UserId,
|
value_objects::UserId,
|
||||||
};
|
};
|
||||||
use k_ap::{ApContentReader, ApObjectHandler};
|
use k_ap::{ApContentReader, ApObjectHandler};
|
||||||
@@ -15,7 +15,7 @@ use crate::urls::{actor_url, goal_url};
|
|||||||
|
|
||||||
pub struct GoalObjectHandler {
|
pub struct GoalObjectHandler {
|
||||||
pub remote_goal_repo: Arc<dyn RemoteGoalRepository>,
|
pub remote_goal_repo: Arc<dyn RemoteGoalRepository>,
|
||||||
pub goal_repo: Arc<dyn GoalRepository>,
|
pub goal_repo: Arc<dyn GoalQuery>,
|
||||||
pub base_url: String,
|
pub base_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ pub struct ActivityPubDeps {
|
|||||||
pub local_ap_content: std::sync::Arc<dyn domain::ports::LocalApContentQuery>,
|
pub local_ap_content: std::sync::Arc<dyn domain::ports::LocalApContentQuery>,
|
||||||
pub movie_repo: std::sync::Arc<dyn domain::ports::MovieQuery>,
|
pub movie_repo: std::sync::Arc<dyn domain::ports::MovieQuery>,
|
||||||
pub review_repo: std::sync::Arc<dyn domain::ports::ReviewRepository>,
|
pub review_repo: std::sync::Arc<dyn domain::ports::ReviewRepository>,
|
||||||
pub diary_repo: std::sync::Arc<dyn domain::ports::DiaryRepository>,
|
pub diary_repo: std::sync::Arc<dyn domain::ports::DiaryQuery>,
|
||||||
pub goal_repo: std::sync::Arc<dyn domain::ports::GoalRepository>,
|
pub goal_repo: std::sync::Arc<dyn domain::ports::GoalQuery>,
|
||||||
pub stats_repo: std::sync::Arc<dyn domain::ports::StatsRepository>,
|
pub stats_repo: std::sync::Arc<dyn domain::ports::StatsRepository>,
|
||||||
pub user_repo: std::sync::Arc<dyn domain::ports::UserRepository>,
|
pub user_repo: std::sync::Arc<dyn domain::ports::UserRepository>,
|
||||||
pub federation_settings: std::sync::Arc<dyn domain::ports::UserFederationSettingsQuery>,
|
pub federation_settings: std::sync::Arc<dyn domain::ports::UserFederationSettingsQuery>,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use async_trait::async_trait;
|
|||||||
use domain::{
|
use domain::{
|
||||||
events::DomainEvent,
|
events::DomainEvent,
|
||||||
models::ReviewSource,
|
models::ReviewSource,
|
||||||
ports::{DiaryRepository, EventPublisher, LocalApContentQuery, MovieQuery},
|
ports::{DiaryQuery, EventPublisher, LocalApContentQuery, MovieQuery},
|
||||||
value_objects::{Comment, ExternalMetadataId, MovieId, Rating, ReviewId, UserId},
|
value_objects::{Comment, ExternalMetadataId, MovieId, Rating, ReviewId, UserId},
|
||||||
};
|
};
|
||||||
use k_ap::{ApContentReader, ApObjectHandler};
|
use k_ap::{ApContentReader, ApObjectHandler};
|
||||||
@@ -17,7 +17,7 @@ use crate::urls::{actor_url, review_url};
|
|||||||
pub struct ReviewObjectHandler {
|
pub struct ReviewObjectHandler {
|
||||||
pub content_query: Arc<dyn LocalApContentQuery>,
|
pub content_query: Arc<dyn LocalApContentQuery>,
|
||||||
pub movie_repo: Arc<dyn MovieQuery>,
|
pub movie_repo: Arc<dyn MovieQuery>,
|
||||||
pub diary_repo: Arc<dyn DiaryRepository>,
|
pub diary_repo: Arc<dyn DiaryQuery>,
|
||||||
pub review_store: Arc<dyn RemoteReviewRepository>,
|
pub review_store: Arc<dyn RemoteReviewRepository>,
|
||||||
pub event_publisher: Arc<dyn EventPublisher>,
|
pub event_publisher: Arc<dyn EventPublisher>,
|
||||||
pub base_url: String,
|
pub base_url: String,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use domain::{
|
|||||||
DiaryEntry, DiaryFilter, FeedEntry, MovieStats, ReviewHistory, ReviewSortBy,
|
DiaryEntry, DiaryFilter, FeedEntry, MovieStats, ReviewHistory, ReviewSortBy,
|
||||||
collections::{PageParams, Paginated},
|
collections::{PageParams, Paginated},
|
||||||
},
|
},
|
||||||
ports::DiaryRepository,
|
ports::DiaryQuery,
|
||||||
value_objects::{MovieId, UserId},
|
value_objects::{MovieId, UserId},
|
||||||
};
|
};
|
||||||
use futures::stream::BoxStream;
|
use futures::stream::BoxStream;
|
||||||
@@ -202,7 +202,7 @@ impl PostgresDiaryRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl DiaryRepository for PostgresDiaryRepository {
|
impl DiaryQuery for PostgresDiaryRepository {
|
||||||
async fn query_diary(
|
async fn query_diary(
|
||||||
&self,
|
&self,
|
||||||
filter: &DiaryFilter,
|
filter: &DiaryFilter,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use async_trait::async_trait;
|
|||||||
use domain::{
|
use domain::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
models::{Goal, GoalType},
|
models::{Goal, GoalType},
|
||||||
ports::GoalRepository,
|
ports::{GoalCommand, GoalQuery},
|
||||||
value_objects::{GoalId, UserId},
|
value_objects::{GoalId, UserId},
|
||||||
};
|
};
|
||||||
use sqlx::{PgPool, Row};
|
use sqlx::{PgPool, Row};
|
||||||
@@ -25,7 +25,7 @@ impl PostgresGoalRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl GoalRepository for PostgresGoalRepository {
|
impl GoalCommand for PostgresGoalRepository {
|
||||||
async fn save(&self, goal: &Goal) -> Result<(), DomainError> {
|
async fn save(&self, goal: &Goal) -> Result<(), DomainError> {
|
||||||
let id = goal.id().value().to_string();
|
let id = goal.id().value().to_string();
|
||||||
let user_id = goal.user_id().value().to_string();
|
let user_id = goal.user_id().value().to_string();
|
||||||
@@ -84,7 +84,10 @@ impl GoalRepository for PostgresGoalRepository {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl GoalQuery for PostgresGoalRepository {
|
||||||
async fn find_by_user_and_year(
|
async fn find_by_user_and_year(
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ pub struct PostgresWireOutput {
|
|||||||
pub movie_command: std::sync::Arc<dyn domain::ports::MovieCommand>,
|
pub movie_command: std::sync::Arc<dyn domain::ports::MovieCommand>,
|
||||||
pub movie_query: std::sync::Arc<dyn domain::ports::MovieQuery>,
|
pub movie_query: std::sync::Arc<dyn domain::ports::MovieQuery>,
|
||||||
pub review: std::sync::Arc<dyn domain::ports::ReviewRepository>,
|
pub review: std::sync::Arc<dyn domain::ports::ReviewRepository>,
|
||||||
pub diary: std::sync::Arc<dyn domain::ports::DiaryRepository>,
|
pub diary: std::sync::Arc<dyn domain::ports::DiaryQuery>,
|
||||||
pub stats: std::sync::Arc<dyn domain::ports::StatsRepository>,
|
pub stats: std::sync::Arc<dyn domain::ports::StatsRepository>,
|
||||||
pub user: std::sync::Arc<dyn domain::ports::UserRepository>,
|
pub user: std::sync::Arc<dyn domain::ports::UserRepository>,
|
||||||
pub import_session: std::sync::Arc<dyn domain::ports::ImportSessionRepository>,
|
pub import_session: std::sync::Arc<dyn domain::ports::ImportSessionRepository>,
|
||||||
@@ -92,7 +92,8 @@ pub struct PostgresWireOutput {
|
|||||||
pub ap_content: std::sync::Arc<dyn domain::ports::LocalApContentQuery>,
|
pub ap_content: std::sync::Arc<dyn domain::ports::LocalApContentQuery>,
|
||||||
pub wrapup_repo: std::sync::Arc<dyn domain::ports::WrapUpRepository>,
|
pub wrapup_repo: std::sync::Arc<dyn domain::ports::WrapUpRepository>,
|
||||||
pub wrapup_stats: std::sync::Arc<dyn domain::ports::WrapUpStatsQuery>,
|
pub wrapup_stats: std::sync::Arc<dyn domain::ports::WrapUpStatsQuery>,
|
||||||
pub goal: std::sync::Arc<dyn domain::ports::GoalRepository>,
|
pub goal_command: std::sync::Arc<dyn domain::ports::GoalCommand>,
|
||||||
|
pub goal_query: std::sync::Arc<dyn domain::ports::GoalQuery>,
|
||||||
pub user_settings: std::sync::Arc<dyn domain::ports::UserSettingsRepository>,
|
pub user_settings: std::sync::Arc<dyn domain::ports::UserSettingsRepository>,
|
||||||
pub federation_settings: std::sync::Arc<dyn domain::ports::UserFederationSettingsQuery>,
|
pub federation_settings: std::sync::Arc<dyn domain::ports::UserFederationSettingsQuery>,
|
||||||
pub remote_goal: std::sync::Arc<dyn domain::ports::RemoteGoalRepository>,
|
pub remote_goal: std::sync::Arc<dyn domain::ports::RemoteGoalRepository>,
|
||||||
@@ -134,7 +135,8 @@ pub async fn wire(database_url: &str) -> anyhow::Result<PostgresWireOutput> {
|
|||||||
ap_content: std::sync::Arc::new(PostgresApContentQuery::new(pool.clone())) as _,
|
ap_content: std::sync::Arc::new(PostgresApContentQuery::new(pool.clone())) as _,
|
||||||
wrapup_repo: std::sync::Arc::new(PostgresWrapUpRepository::new(pool.clone())) as _,
|
wrapup_repo: std::sync::Arc::new(PostgresWrapUpRepository::new(pool.clone())) as _,
|
||||||
wrapup_stats: std::sync::Arc::new(PostgresWrapUpStatsQuery::new(pool.clone())) as _,
|
wrapup_stats: std::sync::Arc::new(PostgresWrapUpStatsQuery::new(pool.clone())) as _,
|
||||||
goal: std::sync::Arc::new(goals::PostgresGoalRepository::new(pool.clone())) as _,
|
goal_command: std::sync::Arc::new(goals::PostgresGoalRepository::new(pool.clone())) as _,
|
||||||
|
goal_query: std::sync::Arc::new(goals::PostgresGoalRepository::new(pool.clone())) as _,
|
||||||
user_settings: std::sync::Arc::clone(&user_settings_repo) as _,
|
user_settings: std::sync::Arc::clone(&user_settings_repo) as _,
|
||||||
federation_settings: user_settings_repo as _,
|
federation_settings: user_settings_repo as _,
|
||||||
remote_goal: std::sync::Arc::new(postgres_federation::PostgresRemoteGoalRepository::new(
|
remote_goal: std::sync::Arc::new(postgres_federation::PostgresRemoteGoalRepository::new(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use domain::{
|
|||||||
DiaryEntry, DiaryFilter, FeedEntry, MovieStats, ReviewHistory, ReviewSortBy,
|
DiaryEntry, DiaryFilter, FeedEntry, MovieStats, ReviewHistory, ReviewSortBy,
|
||||||
collections::{PageParams, Paginated},
|
collections::{PageParams, Paginated},
|
||||||
},
|
},
|
||||||
ports::DiaryRepository,
|
ports::DiaryQuery,
|
||||||
value_objects::{MovieId, UserId},
|
value_objects::{MovieId, UserId},
|
||||||
};
|
};
|
||||||
use futures::stream::BoxStream;
|
use futures::stream::BoxStream;
|
||||||
@@ -181,7 +181,7 @@ impl SqliteDiaryRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl DiaryRepository for SqliteDiaryRepository {
|
impl DiaryQuery for SqliteDiaryRepository {
|
||||||
async fn query_diary(
|
async fn query_diary(
|
||||||
&self,
|
&self,
|
||||||
filter: &DiaryFilter,
|
filter: &DiaryFilter,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use async_trait::async_trait;
|
|||||||
use domain::{
|
use domain::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
models::{Goal, GoalType},
|
models::{Goal, GoalType},
|
||||||
ports::GoalRepository,
|
ports::{GoalCommand, GoalQuery},
|
||||||
value_objects::{GoalId, UserId},
|
value_objects::{GoalId, UserId},
|
||||||
};
|
};
|
||||||
use sqlx::{Row, SqlitePool};
|
use sqlx::{Row, SqlitePool};
|
||||||
@@ -23,7 +23,7 @@ impl SqliteGoalRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl GoalRepository for SqliteGoalRepository {
|
impl GoalCommand for SqliteGoalRepository {
|
||||||
async fn save(&self, goal: &Goal) -> Result<(), DomainError> {
|
async fn save(&self, goal: &Goal) -> Result<(), DomainError> {
|
||||||
let id = goal.id().value().to_string();
|
let id = goal.id().value().to_string();
|
||||||
let user_id = goal.user_id().value().to_string();
|
let user_id = goal.user_id().value().to_string();
|
||||||
@@ -82,7 +82,10 @@ impl GoalRepository for SqliteGoalRepository {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl GoalQuery for SqliteGoalRepository {
|
||||||
async fn find_by_user_and_year(
|
async fn find_by_user_and_year(
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ pub struct SqliteWireOutput {
|
|||||||
pub movie_command: std::sync::Arc<dyn domain::ports::MovieCommand>,
|
pub movie_command: std::sync::Arc<dyn domain::ports::MovieCommand>,
|
||||||
pub movie_query: std::sync::Arc<dyn domain::ports::MovieQuery>,
|
pub movie_query: std::sync::Arc<dyn domain::ports::MovieQuery>,
|
||||||
pub review: std::sync::Arc<dyn domain::ports::ReviewRepository>,
|
pub review: std::sync::Arc<dyn domain::ports::ReviewRepository>,
|
||||||
pub diary: std::sync::Arc<dyn domain::ports::DiaryRepository>,
|
pub diary: std::sync::Arc<dyn domain::ports::DiaryQuery>,
|
||||||
pub stats: std::sync::Arc<dyn domain::ports::StatsRepository>,
|
pub stats: std::sync::Arc<dyn domain::ports::StatsRepository>,
|
||||||
pub user: std::sync::Arc<dyn domain::ports::UserRepository>,
|
pub user: std::sync::Arc<dyn domain::ports::UserRepository>,
|
||||||
pub import_session: std::sync::Arc<dyn domain::ports::ImportSessionRepository>,
|
pub import_session: std::sync::Arc<dyn domain::ports::ImportSessionRepository>,
|
||||||
@@ -88,7 +88,8 @@ pub struct SqliteWireOutput {
|
|||||||
pub ap_content: std::sync::Arc<dyn domain::ports::LocalApContentQuery>,
|
pub ap_content: std::sync::Arc<dyn domain::ports::LocalApContentQuery>,
|
||||||
pub wrapup_repo: std::sync::Arc<dyn domain::ports::WrapUpRepository>,
|
pub wrapup_repo: std::sync::Arc<dyn domain::ports::WrapUpRepository>,
|
||||||
pub wrapup_stats: std::sync::Arc<dyn domain::ports::WrapUpStatsQuery>,
|
pub wrapup_stats: std::sync::Arc<dyn domain::ports::WrapUpStatsQuery>,
|
||||||
pub goal: std::sync::Arc<dyn domain::ports::GoalRepository>,
|
pub goal_command: std::sync::Arc<dyn domain::ports::GoalCommand>,
|
||||||
|
pub goal_query: std::sync::Arc<dyn domain::ports::GoalQuery>,
|
||||||
pub user_settings: std::sync::Arc<dyn domain::ports::UserSettingsRepository>,
|
pub user_settings: std::sync::Arc<dyn domain::ports::UserSettingsRepository>,
|
||||||
pub federation_settings: std::sync::Arc<dyn domain::ports::UserFederationSettingsQuery>,
|
pub federation_settings: std::sync::Arc<dyn domain::ports::UserFederationSettingsQuery>,
|
||||||
pub remote_goal: std::sync::Arc<dyn domain::ports::RemoteGoalRepository>,
|
pub remote_goal: std::sync::Arc<dyn domain::ports::RemoteGoalRepository>,
|
||||||
@@ -137,7 +138,8 @@ pub async fn wire(database_url: &str) -> anyhow::Result<SqliteWireOutput> {
|
|||||||
ap_content: std::sync::Arc::new(SqliteApContentQuery::new(pool.clone())) as _,
|
ap_content: std::sync::Arc::new(SqliteApContentQuery::new(pool.clone())) as _,
|
||||||
wrapup_repo: std::sync::Arc::new(SqliteWrapUpRepository::new(pool.clone())) as _,
|
wrapup_repo: std::sync::Arc::new(SqliteWrapUpRepository::new(pool.clone())) as _,
|
||||||
wrapup_stats: std::sync::Arc::new(SqliteWrapUpStatsQuery::new(pool.clone())) as _,
|
wrapup_stats: std::sync::Arc::new(SqliteWrapUpStatsQuery::new(pool.clone())) as _,
|
||||||
goal: std::sync::Arc::new(goals::SqliteGoalRepository::new(pool.clone())) as _,
|
goal_command: std::sync::Arc::new(goals::SqliteGoalRepository::new(pool.clone())) as _,
|
||||||
|
goal_query: std::sync::Arc::new(goals::SqliteGoalRepository::new(pool.clone())) as _,
|
||||||
user_settings: std::sync::Arc::clone(&user_settings_repo) as _,
|
user_settings: std::sync::Arc::clone(&user_settings_repo) as _,
|
||||||
federation_settings: user_settings_repo as _,
|
federation_settings: user_settings_repo as _,
|
||||||
remote_goal: std::sync::Arc::new(sqlite_federation::SqliteRemoteGoalRepository::new(
|
remote_goal: std::sync::Arc::new(sqlite_federation::SqliteRemoteGoalRepository::new(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use super::*;
|
|||||||
use domain::{
|
use domain::{
|
||||||
models::collections::PageParams,
|
models::collections::PageParams,
|
||||||
models::{FeedSortBy, FollowingFilter},
|
models::{FeedSortBy, FollowingFilter},
|
||||||
ports::DiaryRepository,
|
ports::DiaryQuery,
|
||||||
};
|
};
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use domain::ports::{
|
use domain::ports::{
|
||||||
DiaryRepository, EventPublisher, MovieCommand, MovieProfileRepository, MovieQuery,
|
DiaryQuery, EventPublisher, MovieCommand, MovieProfileRepository, MovieQuery,
|
||||||
ReviewRepository, SocialQueryPort,
|
ReviewRepository, SocialQueryPort,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ use crate::config::AppConfig;
|
|||||||
|
|
||||||
pub struct DeleteReviewDeps {
|
pub struct DeleteReviewDeps {
|
||||||
pub review: Arc<dyn ReviewRepository>,
|
pub review: Arc<dyn ReviewRepository>,
|
||||||
pub diary: Arc<dyn DiaryRepository>,
|
pub diary: Arc<dyn DiaryQuery>,
|
||||||
pub movie_command: Arc<dyn MovieCommand>,
|
pub movie_command: Arc<dyn MovieCommand>,
|
||||||
pub event_publisher: Arc<dyn EventPublisher>,
|
pub event_publisher: Arc<dyn EventPublisher>,
|
||||||
}
|
}
|
||||||
@@ -21,12 +21,12 @@ pub struct EditReviewDeps {
|
|||||||
|
|
||||||
pub struct GetMovieSocialPageDeps {
|
pub struct GetMovieSocialPageDeps {
|
||||||
pub movie_query: Arc<dyn MovieQuery>,
|
pub movie_query: Arc<dyn MovieQuery>,
|
||||||
pub diary: Arc<dyn DiaryRepository>,
|
pub diary: Arc<dyn DiaryQuery>,
|
||||||
pub movie_profile: Arc<dyn MovieProfileRepository>,
|
pub movie_profile: Arc<dyn MovieProfileRepository>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GetActivityFeedDeps {
|
pub struct GetActivityFeedDeps {
|
||||||
pub diary: Arc<dyn DiaryRepository>,
|
pub diary: Arc<dyn DiaryQuery>,
|
||||||
pub social_query: Arc<dyn SocialQueryPort>,
|
pub social_query: Arc<dyn SocialQueryPort>,
|
||||||
pub config: AppConfig,
|
pub config: AppConfig,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use domain::{
|
use domain::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
ports::{DiaryExporter, DiaryRepository},
|
ports::{DiaryExporter, DiaryQuery},
|
||||||
value_objects::UserId,
|
value_objects::UserId,
|
||||||
};
|
};
|
||||||
use futures::stream::BoxStream;
|
use futures::stream::BoxStream;
|
||||||
@@ -11,7 +11,7 @@ use futures::stream::BoxStream;
|
|||||||
use crate::diary::queries::ExportQuery;
|
use crate::diary::queries::ExportQuery;
|
||||||
|
|
||||||
pub fn execute(
|
pub fn execute(
|
||||||
diary: &Arc<dyn DiaryRepository>,
|
diary: &Arc<dyn DiaryQuery>,
|
||||||
diary_exporter: &Arc<dyn DiaryExporter>,
|
diary_exporter: &Arc<dyn DiaryExporter>,
|
||||||
query: ExportQuery,
|
query: ExportQuery,
|
||||||
) -> BoxStream<'static, Result<Bytes, DomainError>> {
|
) -> BoxStream<'static, Result<Bytes, DomainError>> {
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ use domain::{
|
|||||||
DiaryEntry, DiaryFilter, ReviewSortBy,
|
DiaryEntry, DiaryFilter, ReviewSortBy,
|
||||||
collections::{PageParams, Paginated},
|
collections::{PageParams, Paginated},
|
||||||
},
|
},
|
||||||
ports::DiaryRepository,
|
ports::DiaryQuery,
|
||||||
value_objects::{MovieId, UserId},
|
value_objects::{MovieId, UserId},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::diary::queries::GetDiaryQuery;
|
use crate::diary::queries::GetDiaryQuery;
|
||||||
|
|
||||||
pub async fn execute(
|
pub async fn execute(
|
||||||
diary: &Arc<dyn DiaryRepository>,
|
diary: &Arc<dyn DiaryQuery>,
|
||||||
query: GetDiaryQuery,
|
query: GetDiaryQuery,
|
||||||
) -> Result<Paginated<DiaryEntry>, DomainError> {
|
) -> Result<Paginated<DiaryEntry>, DomainError> {
|
||||||
let page = PageParams::new(query.limit, query.offset)?;
|
let page = PageParams::new(query.limit, query.offset)?;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||||||
use domain::{
|
use domain::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
models::ReviewHistory,
|
models::ReviewHistory,
|
||||||
ports::DiaryRepository,
|
ports::DiaryQuery,
|
||||||
services::review_history::{ReviewHistoryAnalyzer, Trend},
|
services::review_history::{ReviewHistoryAnalyzer, Trend},
|
||||||
value_objects::MovieId,
|
value_objects::MovieId,
|
||||||
};
|
};
|
||||||
@@ -11,7 +11,7 @@ use domain::{
|
|||||||
use crate::diary::queries::GetReviewHistoryQuery;
|
use crate::diary::queries::GetReviewHistoryQuery;
|
||||||
|
|
||||||
pub async fn execute(
|
pub async fn execute(
|
||||||
diary: &Arc<dyn DiaryRepository>,
|
diary: &Arc<dyn DiaryQuery>,
|
||||||
query: GetReviewHistoryQuery,
|
query: GetReviewHistoryQuery,
|
||||||
) -> Result<(ReviewHistory, Trend), DomainError> {
|
) -> Result<(ReviewHistory, Trend), DomainError> {
|
||||||
let movie_id = MovieId::from_uuid(query.movie_id);
|
let movie_id = MovieId::from_uuid(query.movie_id);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use domain::{
|
|||||||
models::{Movie, Review},
|
models::{Movie, Review},
|
||||||
ports::{MovieCommand, MovieQuery, ReviewRepository},
|
ports::{MovieCommand, MovieQuery, ReviewRepository},
|
||||||
testing::{
|
testing::{
|
||||||
FakeDiaryRepository, InMemoryMovieRepository, InMemoryReviewRepository, NoopEventPublisher,
|
FakeDiaryQuery, InMemoryMovieRepository, InMemoryReviewRepository, NoopEventPublisher,
|
||||||
},
|
},
|
||||||
value_objects::{MovieId, MovieTitle, Rating, ReleaseYear, UserId},
|
value_objects::{MovieId, MovieTitle, Rating, ReleaseYear, UserId},
|
||||||
};
|
};
|
||||||
@@ -41,7 +41,7 @@ fn make_review(movie_id: MovieId, user_id: UserId) -> Review {
|
|||||||
async fn test_delete_review_removes_it() {
|
async fn test_delete_review_removes_it() {
|
||||||
let movies = InMemoryMovieRepository::new();
|
let movies = InMemoryMovieRepository::new();
|
||||||
let reviews = InMemoryReviewRepository::new();
|
let reviews = InMemoryReviewRepository::new();
|
||||||
let diary = FakeDiaryRepository::new();
|
let diary = FakeDiaryQuery::new();
|
||||||
let events = NoopEventPublisher::new();
|
let events = NoopEventPublisher::new();
|
||||||
|
|
||||||
let movie = make_movie();
|
let movie = make_movie();
|
||||||
@@ -79,7 +79,7 @@ async fn test_delete_review_removes_it() {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_delete_review_wrong_user_is_unauthorized() {
|
async fn test_delete_review_wrong_user_is_unauthorized() {
|
||||||
let reviews = InMemoryReviewRepository::new();
|
let reviews = InMemoryReviewRepository::new();
|
||||||
let diary = FakeDiaryRepository::new();
|
let diary = FakeDiaryQuery::new();
|
||||||
let movies = InMemoryMovieRepository::new();
|
let movies = InMemoryMovieRepository::new();
|
||||||
let events = NoopEventPublisher::new();
|
let events = NoopEventPublisher::new();
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use domain::errors::DomainError;
|
use domain::errors::DomainError;
|
||||||
use domain::testing::{FakeDiaryRepository, NoopSocialQueryPort};
|
use domain::testing::{FakeDiaryQuery, NoopSocialQueryPort};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::AppConfig, diary::deps::GetActivityFeedDeps, diary::get_activity_feed,
|
config::AppConfig, diary::deps::GetActivityFeedDeps, diary::get_activity_feed,
|
||||||
@@ -11,7 +11,7 @@ use crate::{
|
|||||||
|
|
||||||
fn default_deps() -> GetActivityFeedDeps {
|
fn default_deps() -> GetActivityFeedDeps {
|
||||||
GetActivityFeedDeps {
|
GetActivityFeedDeps {
|
||||||
diary: FakeDiaryRepository::new() as _,
|
diary: FakeDiaryQuery::new() as _,
|
||||||
social_query: Arc::new(NoopSocialQueryPort),
|
social_query: Arc::new(NoopSocialQueryPort),
|
||||||
config: TestContextBuilder::new().config,
|
config: TestContextBuilder::new().config,
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,7 @@ async fn following_filter_parses_local_and_remote_urls() {
|
|||||||
let social = Arc::new(FakeSocialWithFollowing(following_urls));
|
let social = Arc::new(FakeSocialWithFollowing(following_urls));
|
||||||
|
|
||||||
let deps = GetActivityFeedDeps {
|
let deps = GetActivityFeedDeps {
|
||||||
diary: FakeDiaryRepository::new() as _,
|
diary: FakeDiaryQuery::new() as _,
|
||||||
social_query: social as _,
|
social_query: social as _,
|
||||||
config: AppConfig {
|
config: AppConfig {
|
||||||
allow_registration: true,
|
allow_registration: true,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use domain::testing::FakeDiaryRepository;
|
use domain::testing::FakeDiaryQuery;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{diary::get_diary, diary::queries::GetDiaryQuery};
|
use crate::{diary::get_diary, diary::queries::GetDiaryQuery};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn returns_empty_page() {
|
async fn returns_empty_page() {
|
||||||
let diary = FakeDiaryRepository::new() as Arc<dyn domain::ports::DiaryRepository>;
|
let diary = FakeDiaryQuery::new() as Arc<dyn domain::ports::DiaryQuery>;
|
||||||
|
|
||||||
let result = get_diary::execute(
|
let result = get_diary::execute(
|
||||||
&diary,
|
&diary,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use uuid::Uuid;
|
|||||||
use domain::{
|
use domain::{
|
||||||
models::Movie,
|
models::Movie,
|
||||||
ports::MovieCommand,
|
ports::MovieCommand,
|
||||||
testing::{FakeDiaryRepository, InMemoryMovieProfileRepository, InMemoryMovieRepository},
|
testing::{FakeDiaryQuery, InMemoryMovieProfileRepository, InMemoryMovieRepository},
|
||||||
value_objects::{MovieTitle, ReleaseYear},
|
value_objects::{MovieTitle, ReleaseYear},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ use crate::{
|
|||||||
async fn fails_when_movie_not_found() {
|
async fn fails_when_movie_not_found() {
|
||||||
let deps = GetMovieSocialPageDeps {
|
let deps = GetMovieSocialPageDeps {
|
||||||
movie_query: InMemoryMovieRepository::new(),
|
movie_query: InMemoryMovieRepository::new(),
|
||||||
diary: FakeDiaryRepository::new() as _,
|
diary: FakeDiaryQuery::new() as _,
|
||||||
movie_profile: InMemoryMovieProfileRepository::new(),
|
movie_profile: InMemoryMovieProfileRepository::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ async fn returns_movie_social_page() {
|
|||||||
|
|
||||||
let deps = GetMovieSocialPageDeps {
|
let deps = GetMovieSocialPageDeps {
|
||||||
movie_query: Arc::clone(&movies) as _,
|
movie_query: Arc::clone(&movies) as _,
|
||||||
diary: FakeDiaryRepository::new() as _,
|
diary: FakeDiaryQuery::new() as _,
|
||||||
movie_profile: InMemoryMovieProfileRepository::new(),
|
movie_profile: InMemoryMovieProfileRepository::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use domain::{
|
use domain::{
|
||||||
models::Movie,
|
models::Movie,
|
||||||
ports::DiaryRepository,
|
ports::DiaryQuery,
|
||||||
services::review_history::Trend,
|
services::review_history::Trend,
|
||||||
value_objects::{MovieTitle, ReleaseYear},
|
value_objects::{MovieTitle, ReleaseYear},
|
||||||
};
|
};
|
||||||
@@ -20,9 +20,9 @@ async fn returns_empty_history() {
|
|||||||
);
|
);
|
||||||
let movie_id = movie.id().value();
|
let movie_id = movie.id().value();
|
||||||
|
|
||||||
let diary = domain::testing::FakeDiaryRepository::new();
|
let diary = domain::testing::FakeDiaryQuery::new();
|
||||||
diary.seed_history(movie, vec![]);
|
diary.seed_history(movie, vec![]);
|
||||||
let diary: Arc<dyn DiaryRepository> = diary;
|
let diary: Arc<dyn DiaryQuery> = diary;
|
||||||
|
|
||||||
let (history, trend) = get_review_history::execute(&diary, GetReviewHistoryQuery { movie_id })
|
let (history, trend) = get_review_history::execute(&diary, GetReviewHistoryQuery { movie_id })
|
||||||
.await
|
.await
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ pub async fn execute(
|
|||||||
) -> Result<GoalWithProgress, DomainError> {
|
) -> Result<GoalWithProgress, DomainError> {
|
||||||
let user_id = UserId::from_uuid(cmd.user_id);
|
let user_id = UserId::from_uuid(cmd.user_id);
|
||||||
|
|
||||||
let existing = deps.goal.find_by_user_and_year(&user_id, cmd.year).await?;
|
let existing = deps.goal_query.find_by_user_and_year(&user_id, cmd.year).await?;
|
||||||
if existing.is_some() {
|
if existing.is_some() {
|
||||||
return Err(DomainError::ValidationError(
|
return Err(DomainError::ValidationError(
|
||||||
"Goal already exists for this year".into(),
|
"Goal already exists for this year".into(),
|
||||||
@@ -26,7 +26,7 @@ pub async fn execute(
|
|||||||
cmd.target_count,
|
cmd.target_count,
|
||||||
GoalType::Movies,
|
GoalType::Movies,
|
||||||
)?;
|
)?;
|
||||||
deps.goal.save(&g).await?;
|
deps.goal_command.save(&g).await?;
|
||||||
|
|
||||||
let current_count = deps.stats.count_reviews_in_year(&user_id, cmd.year).await?;
|
let current_count = deps.stats.count_reviews_in_year(&user_id, cmd.year).await?;
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ pub async fn execute(deps: &GoalCommandDeps, cmd: DeleteGoalCommand) -> Result<(
|
|||||||
let user_id = UserId::from_uuid(cmd.user_id);
|
let user_id = UserId::from_uuid(cmd.user_id);
|
||||||
|
|
||||||
let g = deps
|
let g = deps
|
||||||
.goal
|
.goal_query
|
||||||
.find_by_user_and_year(&user_id, cmd.year)
|
.find_by_user_and_year(&user_id, cmd.year)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| DomainError::NotFound(format!("Goal for year {}", cmd.year)))?;
|
.ok_or_else(|| DomainError::NotFound(format!("Goal for year {}", cmd.year)))?;
|
||||||
|
|
||||||
deps.goal.delete(g.id(), &user_id).await?;
|
deps.goal_command.delete(g.id(), &user_id).await?;
|
||||||
|
|
||||||
deps.event_publisher
|
deps.event_publisher
|
||||||
.publish(&DomainEvent::GoalDeleted {
|
.publish(&DomainEvent::GoalDeleted {
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use domain::ports::{EventPublisher, GoalRepository, StatsRepository};
|
use domain::ports::{EventPublisher, GoalCommand, GoalQuery, StatsRepository};
|
||||||
|
|
||||||
pub struct GoalCommandDeps {
|
pub struct GoalCommandDeps {
|
||||||
pub goal: Arc<dyn GoalRepository>,
|
pub goal_command: Arc<dyn GoalCommand>,
|
||||||
|
pub goal_query: Arc<dyn GoalQuery>,
|
||||||
pub stats: Arc<dyn StatsRepository>,
|
pub stats: Arc<dyn StatsRepository>,
|
||||||
pub event_publisher: Arc<dyn EventPublisher>,
|
pub event_publisher: Arc<dyn EventPublisher>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GoalQueryDeps {
|
pub struct GoalQueryDeps {
|
||||||
pub goal: Arc<dyn GoalRepository>,
|
pub goal_query: Arc<dyn GoalQuery>,
|
||||||
pub stats: Arc<dyn StatsRepository>,
|
pub stats: Arc<dyn StatsRepository>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ pub async fn execute(
|
|||||||
let user_id = UserId::from_uuid(query.user_id);
|
let user_id = UserId::from_uuid(query.user_id);
|
||||||
|
|
||||||
let found = deps
|
let found = deps
|
||||||
.goal
|
.goal_query
|
||||||
.find_by_user_and_year(&user_id, query.year)
|
.find_by_user_and_year(&user_id, query.year)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ pub async fn execute(
|
|||||||
query: ListGoalsQuery,
|
query: ListGoalsQuery,
|
||||||
) -> Result<Vec<GoalWithProgress>, DomainError> {
|
) -> Result<Vec<GoalWithProgress>, DomainError> {
|
||||||
let user_id = UserId::from_uuid(query.user_id);
|
let user_id = UserId::from_uuid(query.user_id);
|
||||||
let goals = deps.goal.list_for_user(&user_id).await?;
|
let goals = deps.goal_query.list_for_user(&user_id).await?;
|
||||||
|
|
||||||
let mut result = Vec::with_capacity(goals.len());
|
let mut result = Vec::with_capacity(goals.len());
|
||||||
for g in goals {
|
for g in goals {
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ async fn creates_goal_and_returns_progress() {
|
|||||||
let stats = FakeStatsRepository::new();
|
let stats = FakeStatsRepository::new();
|
||||||
let events = NoopEventPublisher::new();
|
let events = NoopEventPublisher::new();
|
||||||
let deps = GoalCommandDeps {
|
let deps = GoalCommandDeps {
|
||||||
goal: Arc::clone(&goals) as _,
|
goal_command: Arc::clone(&goals) as _,
|
||||||
|
goal_query: Arc::clone(&goals) as _,
|
||||||
stats: Arc::clone(&stats) as _,
|
stats: Arc::clone(&stats) as _,
|
||||||
event_publisher: Arc::clone(&events) as _,
|
event_publisher: Arc::clone(&events) as _,
|
||||||
};
|
};
|
||||||
@@ -43,7 +44,8 @@ async fn creates_goal_with_review_count() {
|
|||||||
stats.set_review_count(Uuid::nil(), 2025, 5);
|
stats.set_review_count(Uuid::nil(), 2025, 5);
|
||||||
let events = NoopEventPublisher::new();
|
let events = NoopEventPublisher::new();
|
||||||
let deps = GoalCommandDeps {
|
let deps = GoalCommandDeps {
|
||||||
goal: Arc::clone(&goals) as _,
|
goal_command: Arc::clone(&goals) as _,
|
||||||
|
goal_query: Arc::clone(&goals) as _,
|
||||||
stats: Arc::clone(&stats) as _,
|
stats: Arc::clone(&stats) as _,
|
||||||
event_publisher: Arc::clone(&events) as _,
|
event_publisher: Arc::clone(&events) as _,
|
||||||
};
|
};
|
||||||
@@ -68,7 +70,8 @@ async fn emits_goal_created_event() {
|
|||||||
let b = TestContextBuilder::new();
|
let b = TestContextBuilder::new();
|
||||||
let events = NoopEventPublisher::new();
|
let events = NoopEventPublisher::new();
|
||||||
let deps = GoalCommandDeps {
|
let deps = GoalCommandDeps {
|
||||||
goal: b.goal_repo.clone(),
|
goal_command: b.goal_command.clone(),
|
||||||
|
goal_query: b.goal_query.clone(),
|
||||||
stats: b.stats_repo.clone(),
|
stats: b.stats_repo.clone(),
|
||||||
event_publisher: Arc::clone(&events) as _,
|
event_publisher: Arc::clone(&events) as _,
|
||||||
};
|
};
|
||||||
@@ -96,7 +99,8 @@ async fn emits_goal_created_event() {
|
|||||||
async fn rejects_duplicate_year() {
|
async fn rejects_duplicate_year() {
|
||||||
let b = TestContextBuilder::new();
|
let b = TestContextBuilder::new();
|
||||||
let deps = GoalCommandDeps {
|
let deps = GoalCommandDeps {
|
||||||
goal: b.goal_repo.clone(),
|
goal_command: b.goal_command.clone(),
|
||||||
|
goal_query: b.goal_query.clone(),
|
||||||
stats: b.stats_repo.clone(),
|
stats: b.stats_repo.clone(),
|
||||||
event_publisher: b.event_publisher.clone(),
|
event_publisher: b.event_publisher.clone(),
|
||||||
};
|
};
|
||||||
@@ -125,7 +129,8 @@ async fn rejects_duplicate_year() {
|
|||||||
async fn rejects_year_before_2020() {
|
async fn rejects_year_before_2020() {
|
||||||
let b = TestContextBuilder::new();
|
let b = TestContextBuilder::new();
|
||||||
let deps = GoalCommandDeps {
|
let deps = GoalCommandDeps {
|
||||||
goal: b.goal_repo.clone(),
|
goal_command: b.goal_command.clone(),
|
||||||
|
goal_query: b.goal_query.clone(),
|
||||||
stats: b.stats_repo.clone(),
|
stats: b.stats_repo.clone(),
|
||||||
event_publisher: b.event_publisher.clone(),
|
event_publisher: b.event_publisher.clone(),
|
||||||
};
|
};
|
||||||
@@ -146,7 +151,8 @@ async fn rejects_year_before_2020() {
|
|||||||
async fn rejects_zero_target() {
|
async fn rejects_zero_target() {
|
||||||
let b = TestContextBuilder::new();
|
let b = TestContextBuilder::new();
|
||||||
let deps = GoalCommandDeps {
|
let deps = GoalCommandDeps {
|
||||||
goal: b.goal_repo.clone(),
|
goal_command: b.goal_command.clone(),
|
||||||
|
goal_query: b.goal_query.clone(),
|
||||||
stats: b.stats_repo.clone(),
|
stats: b.stats_repo.clone(),
|
||||||
event_publisher: b.event_publisher.clone(),
|
event_publisher: b.event_publisher.clone(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ async fn deletes_existing_goal() {
|
|||||||
let stats = FakeStatsRepository::new();
|
let stats = FakeStatsRepository::new();
|
||||||
let events = NoopEventPublisher::new();
|
let events = NoopEventPublisher::new();
|
||||||
let deps = GoalCommandDeps {
|
let deps = GoalCommandDeps {
|
||||||
goal: Arc::clone(&goals) as _,
|
goal_command: Arc::clone(&goals) as _,
|
||||||
|
goal_query: Arc::clone(&goals) as _,
|
||||||
stats: Arc::clone(&stats) as _,
|
stats: Arc::clone(&stats) as _,
|
||||||
event_publisher: Arc::clone(&events) as _,
|
event_publisher: Arc::clone(&events) as _,
|
||||||
};
|
};
|
||||||
@@ -50,7 +51,8 @@ async fn deletes_existing_goal() {
|
|||||||
async fn fails_when_not_found() {
|
async fn fails_when_not_found() {
|
||||||
let b = TestContextBuilder::new();
|
let b = TestContextBuilder::new();
|
||||||
let deps = GoalCommandDeps {
|
let deps = GoalCommandDeps {
|
||||||
goal: b.goal_repo.clone(),
|
goal_command: b.goal_command.clone(),
|
||||||
|
goal_query: b.goal_query.clone(),
|
||||||
stats: b.stats_repo.clone(),
|
stats: b.stats_repo.clone(),
|
||||||
event_publisher: b.event_publisher.clone(),
|
event_publisher: b.event_publisher.clone(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,12 +8,13 @@ use crate::test_helpers::TestContextBuilder;
|
|||||||
async fn returns_goal_when_exists() {
|
async fn returns_goal_when_exists() {
|
||||||
let b = TestContextBuilder::new();
|
let b = TestContextBuilder::new();
|
||||||
let cmd_deps = GoalCommandDeps {
|
let cmd_deps = GoalCommandDeps {
|
||||||
goal: b.goal_repo.clone(),
|
goal_command: b.goal_command.clone(),
|
||||||
|
goal_query: b.goal_query.clone(),
|
||||||
stats: b.stats_repo.clone(),
|
stats: b.stats_repo.clone(),
|
||||||
event_publisher: b.event_publisher.clone(),
|
event_publisher: b.event_publisher.clone(),
|
||||||
};
|
};
|
||||||
let query_deps = GoalQueryDeps {
|
let query_deps = GoalQueryDeps {
|
||||||
goal: b.goal_repo.clone(),
|
goal_query: b.goal_query.clone(),
|
||||||
stats: b.stats_repo.clone(),
|
stats: b.stats_repo.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -46,7 +47,7 @@ async fn returns_goal_when_exists() {
|
|||||||
async fn returns_none_when_missing() {
|
async fn returns_none_when_missing() {
|
||||||
let b = TestContextBuilder::new();
|
let b = TestContextBuilder::new();
|
||||||
let query_deps = GoalQueryDeps {
|
let query_deps = GoalQueryDeps {
|
||||||
goal: b.goal_repo.clone(),
|
goal_query: b.goal_query.clone(),
|
||||||
stats: b.stats_repo.clone(),
|
stats: b.stats_repo.clone(),
|
||||||
};
|
};
|
||||||
let result = get::execute(
|
let result = get::execute(
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::test_helpers::TestContextBuilder;
|
|||||||
async fn returns_empty_when_no_goals() {
|
async fn returns_empty_when_no_goals() {
|
||||||
let b = TestContextBuilder::new();
|
let b = TestContextBuilder::new();
|
||||||
let query_deps = GoalQueryDeps {
|
let query_deps = GoalQueryDeps {
|
||||||
goal: b.goal_repo.clone(),
|
goal_query: b.goal_query.clone(),
|
||||||
stats: b.stats_repo.clone(),
|
stats: b.stats_repo.clone(),
|
||||||
};
|
};
|
||||||
let result = list::execute(
|
let result = list::execute(
|
||||||
@@ -27,12 +27,13 @@ async fn returns_empty_when_no_goals() {
|
|||||||
async fn returns_all_goals_for_user() {
|
async fn returns_all_goals_for_user() {
|
||||||
let b = TestContextBuilder::new();
|
let b = TestContextBuilder::new();
|
||||||
let cmd_deps = GoalCommandDeps {
|
let cmd_deps = GoalCommandDeps {
|
||||||
goal: b.goal_repo.clone(),
|
goal_command: b.goal_command.clone(),
|
||||||
|
goal_query: b.goal_query.clone(),
|
||||||
stats: b.stats_repo.clone(),
|
stats: b.stats_repo.clone(),
|
||||||
event_publisher: b.event_publisher.clone(),
|
event_publisher: b.event_publisher.clone(),
|
||||||
};
|
};
|
||||||
let query_deps = GoalQueryDeps {
|
let query_deps = GoalQueryDeps {
|
||||||
goal: b.goal_repo.clone(),
|
goal_query: b.goal_query.clone(),
|
||||||
stats: b.stats_repo.clone(),
|
stats: b.stats_repo.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ use crate::test_helpers::TestContextBuilder;
|
|||||||
async fn updates_target_count() {
|
async fn updates_target_count() {
|
||||||
let b = TestContextBuilder::new();
|
let b = TestContextBuilder::new();
|
||||||
let deps = GoalCommandDeps {
|
let deps = GoalCommandDeps {
|
||||||
goal: b.goal_repo.clone(),
|
goal_command: b.goal_command.clone(),
|
||||||
|
goal_query: b.goal_query.clone(),
|
||||||
stats: b.stats_repo.clone(),
|
stats: b.stats_repo.clone(),
|
||||||
event_publisher: b.event_publisher.clone(),
|
event_publisher: b.event_publisher.clone(),
|
||||||
};
|
};
|
||||||
@@ -45,7 +46,8 @@ async fn updates_target_count() {
|
|||||||
async fn fails_when_goal_not_found() {
|
async fn fails_when_goal_not_found() {
|
||||||
let b = TestContextBuilder::new();
|
let b = TestContextBuilder::new();
|
||||||
let deps = GoalCommandDeps {
|
let deps = GoalCommandDeps {
|
||||||
goal: b.goal_repo.clone(),
|
goal_command: b.goal_command.clone(),
|
||||||
|
goal_query: b.goal_query.clone(),
|
||||||
stats: b.stats_repo.clone(),
|
stats: b.stats_repo.clone(),
|
||||||
event_publisher: b.event_publisher.clone(),
|
event_publisher: b.event_publisher.clone(),
|
||||||
};
|
};
|
||||||
@@ -66,7 +68,8 @@ async fn fails_when_goal_not_found() {
|
|||||||
async fn rejects_zero_target() {
|
async fn rejects_zero_target() {
|
||||||
let b = TestContextBuilder::new();
|
let b = TestContextBuilder::new();
|
||||||
let deps = GoalCommandDeps {
|
let deps = GoalCommandDeps {
|
||||||
goal: b.goal_repo.clone(),
|
goal_command: b.goal_command.clone(),
|
||||||
|
goal_query: b.goal_query.clone(),
|
||||||
stats: b.stats_repo.clone(),
|
stats: b.stats_repo.clone(),
|
||||||
event_publisher: b.event_publisher.clone(),
|
event_publisher: b.event_publisher.clone(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ pub async fn execute(
|
|||||||
let user_id = UserId::from_uuid(cmd.user_id);
|
let user_id = UserId::from_uuid(cmd.user_id);
|
||||||
|
|
||||||
let mut g = deps
|
let mut g = deps
|
||||||
.goal
|
.goal_query
|
||||||
.find_by_user_and_year(&user_id, cmd.year)
|
.find_by_user_and_year(&user_id, cmd.year)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| DomainError::NotFound(format!("Goal for year {}", cmd.year)))?;
|
.ok_or_else(|| DomainError::NotFound(format!("Goal for year {}", cmd.year)))?;
|
||||||
|
|
||||||
g.update_target(cmd.target_count)?;
|
g.update_target(cmd.target_count)?;
|
||||||
deps.goal.update(&g).await?;
|
deps.goal_command.update(&g).await?;
|
||||||
|
|
||||||
let current_count = deps.stats.count_reviews_in_year(&user_id, cmd.year).await?;
|
let current_count = deps.stats.count_reviews_in_year(&user_id, cmd.year).await?;
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ use domain::testing::{
|
|||||||
};
|
};
|
||||||
use domain::{
|
use domain::{
|
||||||
ports::{
|
ports::{
|
||||||
AuthService, DiaryExporter, DiaryRepository, DocumentParser, EventPublisher,
|
AuthService, DiaryExporter, DiaryQuery, DocumentParser, EventPublisher,
|
||||||
GoalRepository, ImportProfileRepository, ImportSessionRepository, MetadataClient,
|
GoalCommand, GoalQuery, ImportProfileRepository, ImportSessionRepository, MetadataClient,
|
||||||
MovieCommand, MovieProfileRepository, MovieQuery, ObjectStorage, PasswordHasher,
|
MovieCommand, MovieProfileRepository, MovieQuery, ObjectStorage, PasswordHasher,
|
||||||
PersonCommand, PersonQuery, PosterFetcherClient, RefreshSessionRepository,
|
PersonCommand, PersonQuery, PosterFetcherClient, RefreshSessionRepository,
|
||||||
ReviewRepository, SearchCommand, SearchPort, StatsRepository, UserProfileFieldsRepository,
|
ReviewRepository, SearchCommand, SearchPort, StatsRepository, UserProfileFieldsRepository,
|
||||||
@@ -14,7 +14,7 @@ use domain::{
|
|||||||
WatchlistRepository, WebhookTokenRepository, WrapUpRepository, WrapUpStatsQuery,
|
WatchlistRepository, WebhookTokenRepository, WrapUpRepository, WrapUpStatsQuery,
|
||||||
},
|
},
|
||||||
testing::{
|
testing::{
|
||||||
FakeAuthService, FakeDiaryRepository, FakeDocumentParser, FakeMetadataClient,
|
FakeAuthService, FakeDiaryQuery, FakeDocumentParser, FakeMetadataClient,
|
||||||
FakePasswordHasher, FakePersonQuery, FakePosterFetcher, FakeSearchCommand, FakeSearchPort,
|
FakePasswordHasher, FakePersonQuery, FakePosterFetcher, FakeSearchCommand, FakeSearchPort,
|
||||||
FakeStatsRepository, InMemoryImportProfileRepository, InMemoryImportSessionRepository,
|
FakeStatsRepository, InMemoryImportProfileRepository, InMemoryImportSessionRepository,
|
||||||
InMemoryMovieProfileRepository, InMemoryMovieRepository, InMemoryProfileFieldsRepo,
|
InMemoryMovieProfileRepository, InMemoryMovieRepository, InMemoryProfileFieldsRepo,
|
||||||
@@ -43,7 +43,7 @@ pub struct TestContextBuilder {
|
|||||||
pub movie_command: Arc<dyn MovieCommand>,
|
pub movie_command: Arc<dyn MovieCommand>,
|
||||||
pub movie_query: Arc<dyn MovieQuery>,
|
pub movie_query: Arc<dyn MovieQuery>,
|
||||||
pub review_repo: Arc<dyn ReviewRepository>,
|
pub review_repo: Arc<dyn ReviewRepository>,
|
||||||
pub diary_repo: Arc<dyn DiaryRepository>,
|
pub diary_repo: Arc<dyn DiaryQuery>,
|
||||||
pub diary_exporter: Arc<dyn DiaryExporter>,
|
pub diary_exporter: Arc<dyn DiaryExporter>,
|
||||||
pub document_parser: Arc<dyn DocumentParser>,
|
pub document_parser: Arc<dyn DocumentParser>,
|
||||||
pub stats_repo: Arc<dyn StatsRepository>,
|
pub stats_repo: Arc<dyn StatsRepository>,
|
||||||
@@ -68,7 +68,8 @@ pub struct TestContextBuilder {
|
|||||||
pub search_command: Arc<dyn SearchCommand>,
|
pub search_command: Arc<dyn SearchCommand>,
|
||||||
pub wrapup_stats: Arc<dyn WrapUpStatsQuery>,
|
pub wrapup_stats: Arc<dyn WrapUpStatsQuery>,
|
||||||
pub wrapup_repo: Arc<dyn WrapUpRepository>,
|
pub wrapup_repo: Arc<dyn WrapUpRepository>,
|
||||||
pub goal_repo: Arc<dyn GoalRepository>,
|
pub goal_command: Arc<dyn GoalCommand>,
|
||||||
|
pub goal_query: Arc<dyn GoalQuery>,
|
||||||
pub user_settings_repo: Arc<dyn UserSettingsRepository>,
|
pub user_settings_repo: Arc<dyn UserSettingsRepository>,
|
||||||
pub review_logger: Arc<dyn ReviewLogger>,
|
pub review_logger: Arc<dyn ReviewLogger>,
|
||||||
pub social_query: Arc<dyn domain::ports::SocialQueryPort>,
|
pub social_query: Arc<dyn domain::ports::SocialQueryPort>,
|
||||||
@@ -84,11 +85,14 @@ impl Default for TestContextBuilder {
|
|||||||
|
|
||||||
impl TestContextBuilder {
|
impl TestContextBuilder {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
let movies = InMemoryMovieRepository::new();
|
||||||
|
let watch_events = InMemoryWatchEventRepository::new();
|
||||||
|
let goals = InMemoryGoalRepository::new();
|
||||||
Self {
|
Self {
|
||||||
movie_command: InMemoryMovieRepository::new(),
|
movie_command: Arc::clone(&movies) as _,
|
||||||
movie_query: InMemoryMovieRepository::new(),
|
movie_query: movies as _,
|
||||||
review_repo: InMemoryReviewRepository::new(),
|
review_repo: InMemoryReviewRepository::new(),
|
||||||
diary_repo: FakeDiaryRepository::new(),
|
diary_repo: FakeDiaryQuery::new(),
|
||||||
diary_exporter: Arc::new(PanicDiaryExporter),
|
diary_exporter: Arc::new(PanicDiaryExporter),
|
||||||
document_parser: Arc::new(FakeDocumentParser),
|
document_parser: Arc::new(FakeDocumentParser),
|
||||||
stats_repo: FakeStatsRepository::new(),
|
stats_repo: FakeStatsRepository::new(),
|
||||||
@@ -103,8 +107,8 @@ impl TestContextBuilder {
|
|||||||
import_profile_repo: InMemoryImportProfileRepository::new(),
|
import_profile_repo: InMemoryImportProfileRepository::new(),
|
||||||
movie_profile_repo: InMemoryMovieProfileRepository::new(),
|
movie_profile_repo: InMemoryMovieProfileRepository::new(),
|
||||||
watchlist_repo: InMemoryWatchlistRepository::new(),
|
watchlist_repo: InMemoryWatchlistRepository::new(),
|
||||||
watch_event_command: InMemoryWatchEventRepository::new(),
|
watch_event_command: Arc::clone(&watch_events) as _,
|
||||||
watch_event_query: InMemoryWatchEventRepository::new(),
|
watch_event_query: watch_events as _,
|
||||||
webhook_token_repo: InMemoryWebhookTokenRepository::new(),
|
webhook_token_repo: InMemoryWebhookTokenRepository::new(),
|
||||||
profile_fields_repo: InMemoryProfileFieldsRepo::new(),
|
profile_fields_repo: InMemoryProfileFieldsRepo::new(),
|
||||||
person_command: Arc::new(PanicPersonCommand),
|
person_command: Arc::new(PanicPersonCommand),
|
||||||
@@ -113,7 +117,8 @@ impl TestContextBuilder {
|
|||||||
search_command: Arc::new(FakeSearchCommand),
|
search_command: Arc::new(FakeSearchCommand),
|
||||||
wrapup_stats: InMemoryWrapUpStatsQuery::new(),
|
wrapup_stats: InMemoryWrapUpStatsQuery::new(),
|
||||||
wrapup_repo: InMemoryWrapUpRepository::new(),
|
wrapup_repo: InMemoryWrapUpRepository::new(),
|
||||||
goal_repo: InMemoryGoalRepository::new(),
|
goal_command: Arc::clone(&goals) as _,
|
||||||
|
goal_query: goals as _,
|
||||||
user_settings_repo: InMemoryUserSettingsRepository::new(),
|
user_settings_repo: InMemoryUserSettingsRepository::new(),
|
||||||
review_logger: Arc::new(NoopReviewLogger),
|
review_logger: Arc::new(NoopReviewLogger),
|
||||||
social_query: Arc::new(NoopSocialQueryPort),
|
social_query: Arc::new(NoopSocialQueryPort),
|
||||||
@@ -157,7 +162,7 @@ impl TestContextBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_diary(mut self, r: Arc<dyn DiaryRepository>) -> Self {
|
pub fn with_diary(mut self, r: Arc<dyn DiaryQuery>) -> Self {
|
||||||
self.diary_repo = r;
|
self.diary_repo = r;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@@ -172,8 +177,13 @@ impl TestContextBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_goal(mut self, r: Arc<dyn GoalRepository>) -> Self {
|
pub fn with_goal_command(mut self, r: Arc<dyn GoalCommand>) -> Self {
|
||||||
self.goal_repo = r;
|
self.goal_command = r;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_goal_query(mut self, r: Arc<dyn GoalQuery>) -> Self {
|
||||||
|
self.goal_query = r;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use domain::ports::{
|
use domain::ports::{
|
||||||
DiaryRepository, EventPublisher, ObjectStorage, SocialQueryPort, StatsRepository,
|
DiaryQuery, EventPublisher, ObjectStorage, SocialQueryPort, StatsRepository,
|
||||||
UserRepository,
|
UserRepository,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct GetProfileDeps {
|
pub struct GetProfileDeps {
|
||||||
pub stats: Arc<dyn StatsRepository>,
|
pub stats: Arc<dyn StatsRepository>,
|
||||||
pub diary: Arc<dyn DiaryRepository>,
|
pub diary: Arc<dyn DiaryQuery>,
|
||||||
pub social_query: Arc<dyn SocialQueryPort>,
|
pub social_query: Arc<dyn SocialQueryPort>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait DiaryRepository: Send + Sync {
|
pub trait DiaryQuery: Send + Sync {
|
||||||
async fn query_diary(&self, filter: &DiaryFilter)
|
async fn query_diary(&self, filter: &DiaryFilter)
|
||||||
-> Result<Paginated<DiaryEntry>, DomainError>;
|
-> Result<Paginated<DiaryEntry>, DomainError>;
|
||||||
async fn query_activity_feed(
|
async fn query_activity_feed(
|
||||||
|
|||||||
@@ -7,10 +7,14 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait GoalRepository: Send + Sync {
|
pub trait GoalCommand: Send + Sync {
|
||||||
async fn save(&self, goal: &Goal) -> Result<(), DomainError>;
|
async fn save(&self, goal: &Goal) -> Result<(), DomainError>;
|
||||||
async fn update(&self, goal: &Goal) -> Result<(), DomainError>;
|
async fn update(&self, goal: &Goal) -> Result<(), DomainError>;
|
||||||
async fn delete(&self, id: &GoalId, user_id: &UserId) -> Result<(), DomainError>;
|
async fn delete(&self, id: &GoalId, user_id: &UserId) -> Result<(), DomainError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait GoalQuery: Send + Sync {
|
||||||
async fn find_by_user_and_year(
|
async fn find_by_user_and_year(
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pub mod import;
|
|||||||
pub mod jobs;
|
pub mod jobs;
|
||||||
pub mod media_server;
|
pub mod media_server;
|
||||||
pub mod movie;
|
pub mod movie;
|
||||||
|
pub mod noop;
|
||||||
pub mod person;
|
pub mod person;
|
||||||
pub mod rss;
|
pub mod rss;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
|
|||||||
65
crates/domain/src/ports/noop.rs
Normal file
65
crates/domain/src/ports/noop.rs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use crate::{errors::DomainError, value_objects::UserId};
|
||||||
|
|
||||||
|
// ── NoopRemoteWatchlistRepository ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Stub used when federation is disabled — every operation is a no-op.
|
||||||
|
pub struct NoopRemoteWatchlistRepository;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl super::RemoteWatchlistRepository for NoopRemoteWatchlistRepository {
|
||||||
|
async fn save(&self, _: crate::models::RemoteWatchlistEntry) -> Result<(), DomainError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn remove_by_ap_id(&self, _: &str, _: &str) -> Result<(), DomainError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn get_by_actor_url(
|
||||||
|
&self,
|
||||||
|
_: &str,
|
||||||
|
) -> Result<Vec<crate::models::RemoteWatchlistEntry>, DomainError> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
async fn remove_all_by_actor(&self, _: &str) -> Result<(), DomainError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn get_by_derived_uuid(
|
||||||
|
&self,
|
||||||
|
_: uuid::Uuid,
|
||||||
|
) -> Result<Vec<crate::models::RemoteWatchlistEntry>, DomainError> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── NoopSocialQueryPort ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Stub used when federation is disabled — returns empty results.
|
||||||
|
pub struct NoopSocialQueryPort;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl super::SocialQueryPort for NoopSocialQueryPort {
|
||||||
|
async fn get_accepted_following_urls(
|
||||||
|
&self,
|
||||||
|
_: &UserId,
|
||||||
|
) -> Result<Vec<String>, DomainError> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
async fn list_all_followed_remote_actors(
|
||||||
|
&self,
|
||||||
|
) -> Result<Vec<crate::models::RemoteActorInfo>, DomainError> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
async fn count_following(&self, _: &UserId) -> Result<usize, DomainError> {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
async fn count_accepted_followers(&self, _: &UserId) -> Result<usize, DomainError> {
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
async fn get_pending_followers(
|
||||||
|
&self,
|
||||||
|
_: &UserId,
|
||||||
|
) -> Result<Vec<crate::models::PendingFollowerInfo>, DomainError> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ use crate::{
|
|||||||
collections::{PageParams, Paginated},
|
collections::{PageParams, Paginated},
|
||||||
},
|
},
|
||||||
ports::{
|
ports::{
|
||||||
AuthService, DiaryRepository, DocumentParser, MetadataClient, MovieEnrichmentClient,
|
AuthService, DiaryQuery, DocumentParser, MetadataClient, MovieEnrichmentClient,
|
||||||
PasswordHasher, PersonQuery, PosterFetcherClient, SearchCommand, SearchPort,
|
PasswordHasher, PersonQuery, PosterFetcherClient, SearchCommand, SearchPort,
|
||||||
StatsRepository,
|
StatsRepository,
|
||||||
},
|
},
|
||||||
@@ -81,13 +81,13 @@ impl MetadataClient for FakeMetadataClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── FakeDiaryRepository ───────────────────────────────────────────────────────
|
// ── FakeDiaryQuery ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub struct FakeDiaryRepository {
|
pub struct FakeDiaryQuery {
|
||||||
histories: Mutex<HashMap<Uuid, (Movie, Vec<Review>)>>,
|
histories: Mutex<HashMap<Uuid, (Movie, Vec<Review>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FakeDiaryRepository {
|
impl FakeDiaryQuery {
|
||||||
pub fn new() -> Arc<Self> {
|
pub fn new() -> Arc<Self> {
|
||||||
Arc::new(Self {
|
Arc::new(Self {
|
||||||
histories: Mutex::new(HashMap::new()),
|
histories: Mutex::new(HashMap::new()),
|
||||||
@@ -103,7 +103,7 @@ impl FakeDiaryRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl DiaryRepository for FakeDiaryRepository {
|
impl DiaryQuery for FakeDiaryQuery {
|
||||||
async fn query_diary(
|
async fn query_diary(
|
||||||
&self,
|
&self,
|
||||||
_filter: &DiaryFilter,
|
_filter: &DiaryFilter,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use crate::{
|
|||||||
collections::{PageParams, Paginated},
|
collections::{PageParams, Paginated},
|
||||||
},
|
},
|
||||||
ports::{
|
ports::{
|
||||||
GoalRepository, ImportProfileRepository, ImportSessionRepository, MovieCommand,
|
GoalCommand, GoalQuery, ImportProfileRepository, ImportSessionRepository, MovieCommand,
|
||||||
MovieProfileRepository, MovieQuery, RefreshSessionRepository, ReviewRepository,
|
MovieProfileRepository, MovieQuery, RefreshSessionRepository, ReviewRepository,
|
||||||
UserFederationSettingsQuery, UserProfileFieldsRepository, UserRepository,
|
UserFederationSettingsQuery, UserProfileFieldsRepository, UserRepository,
|
||||||
UserSettingsRepository, WatchEventCommand, WatchEventQuery, WatchlistRepository,
|
UserSettingsRepository, WatchEventCommand, WatchEventQuery, WatchlistRepository,
|
||||||
@@ -357,7 +357,7 @@ impl InMemoryGoalRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl GoalRepository for InMemoryGoalRepository {
|
impl GoalCommand for InMemoryGoalRepository {
|
||||||
async fn save(&self, goal: &Goal) -> Result<(), DomainError> {
|
async fn save(&self, goal: &Goal) -> Result<(), DomainError> {
|
||||||
self.store
|
self.store
|
||||||
.lock()
|
.lock()
|
||||||
@@ -383,7 +383,10 @@ impl GoalRepository for InMemoryGoalRepository {
|
|||||||
self.store.lock().unwrap().remove(&id.value());
|
self.store.lock().unwrap().remove(&id.value());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl GoalQuery for InMemoryGoalRepository {
|
||||||
async fn find_by_user_and_year(
|
async fn find_by_user_and_year(
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
|
|||||||
@@ -62,78 +62,17 @@ impl ObjectStorage for NoopObjectStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── NoopRemoteWatchlistRepository ─────────────────────────────────────────────
|
// Re-export production noop types so test code that imports from
|
||||||
|
// `domain::testing` keeps compiling without changes.
|
||||||
|
pub use crate::ports::noop::NoopRemoteWatchlistRepository;
|
||||||
|
pub use crate::ports::noop::NoopSocialQueryPort;
|
||||||
|
|
||||||
pub struct NoopRemoteWatchlistRepository;
|
// ── NoopGoalCommand ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
pub struct NoopGoalCommand;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl crate::ports::RemoteWatchlistRepository for NoopRemoteWatchlistRepository {
|
impl crate::ports::GoalCommand for NoopGoalCommand {
|
||||||
async fn save(&self, _: crate::models::RemoteWatchlistEntry) -> Result<(), DomainError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
async fn remove_by_ap_id(&self, _: &str, _: &str) -> Result<(), DomainError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
async fn get_by_actor_url(
|
|
||||||
&self,
|
|
||||||
_: &str,
|
|
||||||
) -> Result<Vec<crate::models::RemoteWatchlistEntry>, DomainError> {
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
|
||||||
async fn remove_all_by_actor(&self, _: &str) -> Result<(), DomainError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
async fn get_by_derived_uuid(
|
|
||||||
&self,
|
|
||||||
_: uuid::Uuid,
|
|
||||||
) -> Result<Vec<crate::models::RemoteWatchlistEntry>, DomainError> {
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── NoopSocialQueryPort ───────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
pub struct NoopSocialQueryPort;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl crate::ports::SocialQueryPort for NoopSocialQueryPort {
|
|
||||||
async fn get_accepted_following_urls(
|
|
||||||
&self,
|
|
||||||
_: &crate::value_objects::UserId,
|
|
||||||
) -> Result<Vec<String>, DomainError> {
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
|
||||||
async fn list_all_followed_remote_actors(
|
|
||||||
&self,
|
|
||||||
) -> Result<Vec<crate::models::RemoteActorInfo>, DomainError> {
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
|
||||||
async fn count_following(
|
|
||||||
&self,
|
|
||||||
_: &crate::value_objects::UserId,
|
|
||||||
) -> Result<usize, DomainError> {
|
|
||||||
Ok(0)
|
|
||||||
}
|
|
||||||
async fn count_accepted_followers(
|
|
||||||
&self,
|
|
||||||
_: &crate::value_objects::UserId,
|
|
||||||
) -> Result<usize, DomainError> {
|
|
||||||
Ok(0)
|
|
||||||
}
|
|
||||||
async fn get_pending_followers(
|
|
||||||
&self,
|
|
||||||
_: &crate::value_objects::UserId,
|
|
||||||
) -> Result<Vec<crate::models::PendingFollowerInfo>, DomainError> {
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── NoopGoalRepository ────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
pub struct NoopGoalRepository;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl crate::ports::GoalRepository for NoopGoalRepository {
|
|
||||||
async fn save(&self, _: &crate::models::Goal) -> Result<(), DomainError> {
|
async fn save(&self, _: &crate::models::Goal) -> Result<(), DomainError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -147,6 +86,14 @@ impl crate::ports::GoalRepository for NoopGoalRepository {
|
|||||||
) -> Result<(), DomainError> {
|
) -> Result<(), DomainError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── NoopGoalQuery ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
pub struct NoopGoalQuery;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl crate::ports::GoalQuery for NoopGoalQuery {
|
||||||
async fn find_by_user_and_year(
|
async fn find_by_user_and_year(
|
||||||
&self,
|
&self,
|
||||||
_: &UserId,
|
_: &UserId,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use crate::{
|
|||||||
collections::{PageParams, Paginated},
|
collections::{PageParams, Paginated},
|
||||||
},
|
},
|
||||||
ports::{
|
ports::{
|
||||||
DiaryExporter, DiaryRepository, DocumentParser, ImportProfileRepository,
|
DiaryExporter, DiaryQuery, DocumentParser, ImportProfileRepository,
|
||||||
ImportSessionRepository, MovieProfileRepository, PersonCommand, PersonQuery,
|
ImportSessionRepository, MovieProfileRepository, PersonCommand, PersonQuery,
|
||||||
PosterFetcherClient, RefreshSessionRepository, SearchCommand, SearchPort, StatsRepository,
|
PosterFetcherClient, RefreshSessionRepository, SearchCommand, SearchPort, StatsRepository,
|
||||||
UserProfileFieldsRepository,
|
UserProfileFieldsRepository,
|
||||||
@@ -19,20 +19,20 @@ use crate::{
|
|||||||
value_objects::{ImportProfileId, ImportSessionId, MovieId, PosterUrl, UserId},
|
value_objects::{ImportProfileId, ImportSessionId, MovieId, PosterUrl, UserId},
|
||||||
};
|
};
|
||||||
|
|
||||||
// ── PanicDiaryRepository ──────────────────────────────────────────────────────
|
// ── PanicDiaryQuery ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub struct PanicDiaryRepository;
|
pub struct PanicDiaryQuery;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl DiaryRepository for PanicDiaryRepository {
|
impl DiaryQuery for PanicDiaryQuery {
|
||||||
async fn query_diary(&self, _: &DiaryFilter) -> Result<Paginated<DiaryEntry>, DomainError> {
|
async fn query_diary(&self, _: &DiaryFilter) -> Result<Paginated<DiaryEntry>, DomainError> {
|
||||||
panic!("PanicDiaryRepository called")
|
panic!("PanicDiaryQuery called")
|
||||||
}
|
}
|
||||||
async fn query_activity_feed(
|
async fn query_activity_feed(
|
||||||
&self,
|
&self,
|
||||||
_: &PageParams,
|
_: &PageParams,
|
||||||
) -> Result<Paginated<FeedEntry>, DomainError> {
|
) -> Result<Paginated<FeedEntry>, DomainError> {
|
||||||
panic!("PanicDiaryRepository called")
|
panic!("PanicDiaryQuery called")
|
||||||
}
|
}
|
||||||
async fn query_activity_feed_filtered(
|
async fn query_activity_feed_filtered(
|
||||||
&self,
|
&self,
|
||||||
@@ -41,32 +41,32 @@ impl DiaryRepository for PanicDiaryRepository {
|
|||||||
_: Option<&str>,
|
_: Option<&str>,
|
||||||
_: Option<&FollowingFilter>,
|
_: Option<&FollowingFilter>,
|
||||||
) -> Result<Paginated<FeedEntry>, DomainError> {
|
) -> Result<Paginated<FeedEntry>, DomainError> {
|
||||||
panic!("PanicDiaryRepository called")
|
panic!("PanicDiaryQuery called")
|
||||||
}
|
}
|
||||||
async fn get_review_history(&self, _: &MovieId) -> Result<ReviewHistory, DomainError> {
|
async fn get_review_history(&self, _: &MovieId) -> Result<ReviewHistory, DomainError> {
|
||||||
panic!("PanicDiaryRepository called")
|
panic!("PanicDiaryQuery called")
|
||||||
}
|
}
|
||||||
async fn get_user_history(&self, _: &UserId) -> Result<Vec<DiaryEntry>, DomainError> {
|
async fn get_user_history(&self, _: &UserId) -> Result<Vec<DiaryEntry>, DomainError> {
|
||||||
panic!("PanicDiaryRepository called")
|
panic!("PanicDiaryQuery called")
|
||||||
}
|
}
|
||||||
fn stream_user_history(
|
fn stream_user_history(
|
||||||
&self,
|
&self,
|
||||||
_: UserId,
|
_: UserId,
|
||||||
) -> futures::stream::BoxStream<'static, Result<DiaryEntry, DomainError>> {
|
) -> futures::stream::BoxStream<'static, Result<DiaryEntry, DomainError>> {
|
||||||
panic!("PanicDiaryRepository called")
|
panic!("PanicDiaryQuery called")
|
||||||
}
|
}
|
||||||
async fn get_movie_stats(&self, _: &MovieId) -> Result<MovieStats, DomainError> {
|
async fn get_movie_stats(&self, _: &MovieId) -> Result<MovieStats, DomainError> {
|
||||||
panic!("PanicDiaryRepository called")
|
panic!("PanicDiaryQuery called")
|
||||||
}
|
}
|
||||||
async fn get_movie_social_feed(
|
async fn get_movie_social_feed(
|
||||||
&self,
|
&self,
|
||||||
_: &MovieId,
|
_: &MovieId,
|
||||||
_: &PageParams,
|
_: &PageParams,
|
||||||
) -> Result<Paginated<FeedEntry>, DomainError> {
|
) -> Result<Paginated<FeedEntry>, DomainError> {
|
||||||
panic!("PanicDiaryRepository called")
|
panic!("PanicDiaryQuery called")
|
||||||
}
|
}
|
||||||
async fn count_local_posts(&self) -> Result<u64, DomainError> {
|
async fn count_local_posts(&self) -> Result<u64, DomainError> {
|
||||||
panic!("PanicDiaryRepository called")
|
panic!("PanicDiaryQuery called")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ async-trait = { workspace = true }
|
|||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
|
|
||||||
api-types = { workspace = true }
|
api-types = { workspace = true }
|
||||||
domain = { workspace = true, features = ["test-helpers"] }
|
domain = { workspace = true }
|
||||||
application = { workspace = true }
|
application = { workspace = true }
|
||||||
auth = { workspace = true }
|
auth = { workspace = true }
|
||||||
metadata = { workspace = true }
|
metadata = { workspace = true }
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use domain::ports::{
|
use domain::ports::{
|
||||||
AuthService, DiaryExporter, DiaryRepository, DocumentParser, EventPublisher,
|
AuthService, DiaryExporter, DiaryQuery, DocumentParser, EventPublisher,
|
||||||
FederatedProfileQuery, GoalRepository, ImportProfileRepository, ImportSessionRepository,
|
FederatedProfileQuery, GoalCommand, GoalQuery, ImportProfileRepository, ImportSessionRepository,
|
||||||
MetadataClient, MovieCommand, MovieProfileRepository, MovieQuery, ObjectStorage,
|
MetadataClient, MovieCommand, MovieProfileRepository, MovieQuery, ObjectStorage,
|
||||||
PasswordHasher, PersonCommand, PersonEnrichmentClient, PersonQuery, PosterFetcherClient,
|
PasswordHasher, PersonCommand, PersonEnrichmentClient, PersonQuery, PosterFetcherClient,
|
||||||
RefreshSessionRepository, RemoteGoalRepository, RemoteWatchlistRepository, ReviewRepository,
|
RefreshSessionRepository, RemoteGoalRepository, RemoteWatchlistRepository, ReviewRepository,
|
||||||
@@ -19,7 +19,7 @@ pub struct Repositories {
|
|||||||
pub movie_command: Arc<dyn MovieCommand>,
|
pub movie_command: Arc<dyn MovieCommand>,
|
||||||
pub movie_query: Arc<dyn MovieQuery>,
|
pub movie_query: Arc<dyn MovieQuery>,
|
||||||
pub review: Arc<dyn ReviewRepository>,
|
pub review: Arc<dyn ReviewRepository>,
|
||||||
pub diary: Arc<dyn DiaryRepository>,
|
pub diary: Arc<dyn DiaryQuery>,
|
||||||
pub stats: Arc<dyn StatsRepository>,
|
pub stats: Arc<dyn StatsRepository>,
|
||||||
pub user: Arc<dyn UserRepository>,
|
pub user: Arc<dyn UserRepository>,
|
||||||
pub import_session: Arc<dyn ImportSessionRepository>,
|
pub import_session: Arc<dyn ImportSessionRepository>,
|
||||||
@@ -38,7 +38,8 @@ pub struct Repositories {
|
|||||||
pub social_query: Arc<dyn SocialQueryPort>,
|
pub social_query: Arc<dyn SocialQueryPort>,
|
||||||
pub wrapup_stats: Arc<dyn WrapUpStatsQuery>,
|
pub wrapup_stats: Arc<dyn WrapUpStatsQuery>,
|
||||||
pub wrapup_repo: Arc<dyn WrapUpRepository>,
|
pub wrapup_repo: Arc<dyn WrapUpRepository>,
|
||||||
pub goal: Arc<dyn GoalRepository>,
|
pub goal_command: Arc<dyn GoalCommand>,
|
||||||
|
pub goal_query: Arc<dyn GoalQuery>,
|
||||||
pub user_settings: Arc<dyn UserSettingsRepository>,
|
pub user_settings: Arc<dyn UserSettingsRepository>,
|
||||||
pub remote_goal: Arc<dyn RemoteGoalRepository>,
|
pub remote_goal: Arc<dyn RemoteGoalRepository>,
|
||||||
pub refresh_session: Arc<dyn RefreshSessionRepository>,
|
pub refresh_session: Arc<dyn RefreshSessionRepository>,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ pub struct DatabaseOutput {
|
|||||||
pub movie_command: Arc<dyn domain::ports::MovieCommand>,
|
pub movie_command: Arc<dyn domain::ports::MovieCommand>,
|
||||||
pub movie_query: Arc<dyn domain::ports::MovieQuery>,
|
pub movie_query: Arc<dyn domain::ports::MovieQuery>,
|
||||||
pub review: Arc<dyn domain::ports::ReviewRepository>,
|
pub review: Arc<dyn domain::ports::ReviewRepository>,
|
||||||
pub diary: Arc<dyn domain::ports::DiaryRepository>,
|
pub diary: Arc<dyn domain::ports::DiaryQuery>,
|
||||||
pub stats: Arc<dyn domain::ports::StatsRepository>,
|
pub stats: Arc<dyn domain::ports::StatsRepository>,
|
||||||
pub user: Arc<dyn domain::ports::UserRepository>,
|
pub user: Arc<dyn domain::ports::UserRepository>,
|
||||||
pub import_session: Arc<dyn domain::ports::ImportSessionRepository>,
|
pub import_session: Arc<dyn domain::ports::ImportSessionRepository>,
|
||||||
@@ -31,7 +31,8 @@ pub struct DatabaseOutput {
|
|||||||
pub ap_content: Arc<dyn LocalApContentQuery>,
|
pub ap_content: Arc<dyn LocalApContentQuery>,
|
||||||
pub wrapup_stats: Arc<dyn domain::ports::WrapUpStatsQuery>,
|
pub wrapup_stats: Arc<dyn domain::ports::WrapUpStatsQuery>,
|
||||||
pub wrapup_repo: Arc<dyn domain::ports::WrapUpRepository>,
|
pub wrapup_repo: Arc<dyn domain::ports::WrapUpRepository>,
|
||||||
pub goal: Arc<dyn domain::ports::GoalRepository>,
|
pub goal_command: Arc<dyn domain::ports::GoalCommand>,
|
||||||
|
pub goal_query: Arc<dyn domain::ports::GoalQuery>,
|
||||||
pub user_settings: Arc<dyn domain::ports::UserSettingsRepository>,
|
pub user_settings: Arc<dyn domain::ports::UserSettingsRepository>,
|
||||||
pub federation_settings: std::sync::Arc<dyn domain::ports::UserFederationSettingsQuery>,
|
pub federation_settings: std::sync::Arc<dyn domain::ports::UserFederationSettingsQuery>,
|
||||||
pub remote_goal: Arc<dyn domain::ports::RemoteGoalRepository>,
|
pub remote_goal: Arc<dyn domain::ports::RemoteGoalRepository>,
|
||||||
@@ -75,7 +76,8 @@ pub async fn build_database_adapters(backend: &str, url: &str) -> anyhow::Result
|
|||||||
ap_content: w.ap_content,
|
ap_content: w.ap_content,
|
||||||
wrapup_stats: w.wrapup_stats,
|
wrapup_stats: w.wrapup_stats,
|
||||||
wrapup_repo: w.wrapup_repo,
|
wrapup_repo: w.wrapup_repo,
|
||||||
goal: w.goal,
|
goal_command: w.goal_command,
|
||||||
|
goal_query: w.goal_query,
|
||||||
user_settings: w.user_settings,
|
user_settings: w.user_settings,
|
||||||
federation_settings: w.federation_settings,
|
federation_settings: w.federation_settings,
|
||||||
remote_goal: w.remote_goal,
|
remote_goal: w.remote_goal,
|
||||||
@@ -118,7 +120,8 @@ pub async fn build_database_adapters(backend: &str, url: &str) -> anyhow::Result
|
|||||||
ap_content: w.ap_content,
|
ap_content: w.ap_content,
|
||||||
wrapup_stats: w.wrapup_stats,
|
wrapup_stats: w.wrapup_stats,
|
||||||
wrapup_repo: w.wrapup_repo,
|
wrapup_repo: w.wrapup_repo,
|
||||||
goal: w.goal,
|
goal_command: w.goal_command,
|
||||||
|
goal_query: w.goal_query,
|
||||||
user_settings: w.user_settings,
|
user_settings: w.user_settings,
|
||||||
federation_settings: w.federation_settings,
|
federation_settings: w.federation_settings,
|
||||||
remote_goal: w.remote_goal,
|
remote_goal: w.remote_goal,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ pub async fn list_goals(
|
|||||||
user: AuthenticatedUser,
|
user: AuthenticatedUser,
|
||||||
) -> Result<Json<GoalsResponse>, ApiError> {
|
) -> Result<Json<GoalsResponse>, ApiError> {
|
||||||
let deps = GoalQueryDeps {
|
let deps = GoalQueryDeps {
|
||||||
goal: state.app_ctx.repos.goal.clone(),
|
goal_query: state.app_ctx.repos.goal_query.clone(),
|
||||||
stats: state.app_ctx.repos.stats.clone(),
|
stats: state.app_ctx.repos.stats.clone(),
|
||||||
};
|
};
|
||||||
let goals = application::goals::list::execute(
|
let goals = application::goals::list::execute(
|
||||||
@@ -70,7 +70,8 @@ pub async fn create_goal(
|
|||||||
Json(req): Json<CreateGoalRequest>,
|
Json(req): Json<CreateGoalRequest>,
|
||||||
) -> Result<Json<GoalDto>, ApiError> {
|
) -> Result<Json<GoalDto>, ApiError> {
|
||||||
let deps = GoalCommandDeps {
|
let deps = GoalCommandDeps {
|
||||||
goal: state.app_ctx.repos.goal.clone(),
|
goal_command: state.app_ctx.repos.goal_command.clone(),
|
||||||
|
goal_query: state.app_ctx.repos.goal_query.clone(),
|
||||||
stats: state.app_ctx.repos.stats.clone(),
|
stats: state.app_ctx.repos.stats.clone(),
|
||||||
event_publisher: state.app_ctx.services.event_publisher.clone(),
|
event_publisher: state.app_ctx.services.event_publisher.clone(),
|
||||||
};
|
};
|
||||||
@@ -103,7 +104,8 @@ pub async fn update_goal(
|
|||||||
Json(req): Json<UpdateGoalRequest>,
|
Json(req): Json<UpdateGoalRequest>,
|
||||||
) -> Result<Json<GoalDto>, ApiError> {
|
) -> Result<Json<GoalDto>, ApiError> {
|
||||||
let deps = GoalCommandDeps {
|
let deps = GoalCommandDeps {
|
||||||
goal: state.app_ctx.repos.goal.clone(),
|
goal_command: state.app_ctx.repos.goal_command.clone(),
|
||||||
|
goal_query: state.app_ctx.repos.goal_query.clone(),
|
||||||
stats: state.app_ctx.repos.stats.clone(),
|
stats: state.app_ctx.repos.stats.clone(),
|
||||||
event_publisher: state.app_ctx.services.event_publisher.clone(),
|
event_publisher: state.app_ctx.services.event_publisher.clone(),
|
||||||
};
|
};
|
||||||
@@ -134,7 +136,8 @@ pub async fn delete_goal(
|
|||||||
Path(year): Path<u16>,
|
Path(year): Path<u16>,
|
||||||
) -> Result<StatusCode, ApiError> {
|
) -> Result<StatusCode, ApiError> {
|
||||||
let deps = GoalCommandDeps {
|
let deps = GoalCommandDeps {
|
||||||
goal: state.app_ctx.repos.goal.clone(),
|
goal_command: state.app_ctx.repos.goal_command.clone(),
|
||||||
|
goal_query: state.app_ctx.repos.goal_query.clone(),
|
||||||
stats: state.app_ctx.repos.stats.clone(),
|
stats: state.app_ctx.repos.stats.clone(),
|
||||||
event_publisher: state.app_ctx.services.event_publisher.clone(),
|
event_publisher: state.app_ctx.services.event_publisher.clone(),
|
||||||
};
|
};
|
||||||
@@ -163,7 +166,7 @@ pub async fn get_user_goals(
|
|||||||
Path(user_id): Path<Uuid>,
|
Path(user_id): Path<Uuid>,
|
||||||
) -> Result<Json<GoalsResponse>, ApiError> {
|
) -> Result<Json<GoalsResponse>, ApiError> {
|
||||||
let deps = GoalQueryDeps {
|
let deps = GoalQueryDeps {
|
||||||
goal: state.app_ctx.repos.goal.clone(),
|
goal_query: state.app_ctx.repos.goal_query.clone(),
|
||||||
stats: state.app_ctx.repos.stats.clone(),
|
stats: state.app_ctx.repos.stats.clone(),
|
||||||
};
|
};
|
||||||
let goals = application::goals::list::execute(
|
let goals = application::goals::list::execute(
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ pub async fn get_user_profile(
|
|||||||
goals: {
|
goals: {
|
||||||
let goals_list = application::goals::list::execute(
|
let goals_list = application::goals::list::execute(
|
||||||
&application::goals::deps::GoalQueryDeps {
|
&application::goals::deps::GoalQueryDeps {
|
||||||
goal: state.app_ctx.repos.goal.clone(),
|
goal_query: state.app_ctx.repos.goal_query.clone(),
|
||||||
stats: state.app_ctx.repos.stats.clone(),
|
stats: state.app_ctx.repos.stats.clone(),
|
||||||
},
|
},
|
||||||
application::goals::queries::ListGoalsQuery { user_id },
|
application::goals::queries::ListGoalsQuery { user_id },
|
||||||
@@ -531,6 +531,107 @@ pub async fn get_user_by_username(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Profile helpers (private) ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
struct PaginationInfo {
|
||||||
|
offset: u32,
|
||||||
|
has_more: bool,
|
||||||
|
limit: u32,
|
||||||
|
page_items: Vec<template_askama::PageItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_pagination(
|
||||||
|
entries: Option<&domain::models::collections::Paginated<domain::models::DiaryEntry>>,
|
||||||
|
) -> PaginationInfo {
|
||||||
|
let (offset, has_more, limit) = entries
|
||||||
|
.map(|e| {
|
||||||
|
let has_more = (e.offset as u64).saturating_add(e.limit as u64) < e.total_count;
|
||||||
|
(e.offset, has_more, e.limit)
|
||||||
|
})
|
||||||
|
.unwrap_or((0, false, super::DEFAULT_PAGE_LIMIT));
|
||||||
|
let total = entries.map(|e| e.total_count as u32).unwrap_or(0);
|
||||||
|
let total_pages = total
|
||||||
|
.saturating_add(limit.saturating_sub(1))
|
||||||
|
.checked_div(limit)
|
||||||
|
.unwrap_or(1);
|
||||||
|
let current_page = offset.checked_div(limit).unwrap_or(0);
|
||||||
|
let page_items = build_page_items(total_pages, current_page);
|
||||||
|
PaginationInfo {
|
||||||
|
offset,
|
||||||
|
has_more,
|
||||||
|
limit,
|
||||||
|
page_items,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StatsDisplay {
|
||||||
|
avg_rating: String,
|
||||||
|
favorite_director: String,
|
||||||
|
most_active_month: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_stats_display(stats: &domain::models::UserStats) -> StatsDisplay {
|
||||||
|
StatsDisplay {
|
||||||
|
avg_rating: stats
|
||||||
|
.avg_rating
|
||||||
|
.map(|r| format!("{:.1}", r))
|
||||||
|
.unwrap_or_else(|| "\u{2014}".to_string()),
|
||||||
|
favorite_director: stats
|
||||||
|
.favorite_director
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| "\u{2014}".to_string()),
|
||||||
|
most_active_month: stats
|
||||||
|
.most_active_month
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| "\u{2014}".to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_monthly_rating_rows<'a>(
|
||||||
|
trends: Option<&'a domain::models::UserTrends>,
|
||||||
|
) -> Vec<MonthlyRatingRow<'a>> {
|
||||||
|
trends
|
||||||
|
.map(|t| {
|
||||||
|
t.monthly_ratings
|
||||||
|
.iter()
|
||||||
|
.map(|r| MonthlyRatingRow {
|
||||||
|
rating: r,
|
||||||
|
bar_height_px: bar_height_px(r.avg_rating),
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_profile_goals(
|
||||||
|
state: &AppState,
|
||||||
|
user_id: Uuid,
|
||||||
|
) -> Vec<template_askama::GoalViewData> {
|
||||||
|
let goals_list = application::goals::list::execute(
|
||||||
|
&application::goals::deps::GoalQueryDeps {
|
||||||
|
goal_query: state.app_ctx.repos.goal_query.clone(),
|
||||||
|
stats: state.app_ctx.repos.stats.clone(),
|
||||||
|
},
|
||||||
|
application::goals::queries::ListGoalsQuery {
|
||||||
|
user_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap_or_default();
|
||||||
|
goals_list
|
||||||
|
.iter()
|
||||||
|
.map(|g| template_askama::GoalViewData {
|
||||||
|
year: g.goal.year(),
|
||||||
|
target_count: g.goal.target_count(),
|
||||||
|
current_count: g.current_count,
|
||||||
|
percentage: g.percentage().round(),
|
||||||
|
is_complete: g.is_complete(),
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Handler ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub async fn get_user_profile_html(
|
pub async fn get_user_profile_html(
|
||||||
OptionalCookieUser(user_id): OptionalCookieUser,
|
OptionalCookieUser(user_id): OptionalCookieUser,
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
@@ -634,60 +735,16 @@ pub async fn get_user_profile_html(
|
|||||||
};
|
};
|
||||||
match application::users::get_profile::execute(&html_profile_deps, query).await {
|
match application::users::get_profile::execute(&html_profile_deps, query).await {
|
||||||
Ok(profile) => {
|
Ok(profile) => {
|
||||||
let (offset, has_more, limit) = profile
|
let pag = compute_pagination(profile.entries.as_ref());
|
||||||
.entries
|
|
||||||
.as_ref()
|
|
||||||
.map(|e| {
|
|
||||||
let has_more = (e.offset as u64).saturating_add(e.limit as u64) < e.total_count;
|
|
||||||
(e.offset, has_more, e.limit)
|
|
||||||
})
|
|
||||||
.unwrap_or((0, false, super::DEFAULT_PAGE_LIMIT));
|
|
||||||
if !is_own_profile {
|
if !is_own_profile {
|
||||||
ctx.page_rss_url = Some(format!("/users/{}/feed.rss", profile_user_uuid));
|
ctx.page_rss_url = Some(format!("/users/{}/feed.rss", profile_user_uuid));
|
||||||
}
|
}
|
||||||
let email = profile_user.email().value().to_string();
|
let email = profile_user.email().value().to_string();
|
||||||
let display_name = email.split('@').next().unwrap_or("?").to_string();
|
let display_name = email.split('@').next().unwrap_or("?").to_string();
|
||||||
let avg_rating_display = profile
|
let stats_disp = build_stats_display(&profile.stats);
|
||||||
.stats
|
|
||||||
.avg_rating
|
|
||||||
.map(|r| format!("{:.1}", r))
|
|
||||||
.unwrap_or_else(|| "\u{2014}".to_string());
|
|
||||||
let favorite_director_display = profile
|
|
||||||
.stats
|
|
||||||
.favorite_director
|
|
||||||
.clone()
|
|
||||||
.unwrap_or_else(|| "\u{2014}".to_string());
|
|
||||||
let most_active_month_display = profile
|
|
||||||
.stats
|
|
||||||
.most_active_month
|
|
||||||
.clone()
|
|
||||||
.unwrap_or_else(|| "\u{2014}".to_string());
|
|
||||||
let history = profile.history.map(application::users::group_by_month);
|
let history = profile.history.map(application::users::group_by_month);
|
||||||
let heatmap = history.as_deref().map(build_heatmap).unwrap_or_default();
|
let heatmap = history.as_deref().map(build_heatmap).unwrap_or_default();
|
||||||
let monthly_rating_rows: Vec<MonthlyRatingRow<'_>> = profile
|
let monthly_rating_rows = build_monthly_rating_rows(profile.trends.as_ref());
|
||||||
.trends
|
|
||||||
.as_ref()
|
|
||||||
.map(|t| {
|
|
||||||
t.monthly_ratings
|
|
||||||
.iter()
|
|
||||||
.map(|r| MonthlyRatingRow {
|
|
||||||
rating: r,
|
|
||||||
bar_height_px: bar_height_px(r.avg_rating),
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
let total = profile
|
|
||||||
.entries
|
|
||||||
.as_ref()
|
|
||||||
.map(|e| e.total_count as u32)
|
|
||||||
.unwrap_or(0);
|
|
||||||
let total_pages = total
|
|
||||||
.saturating_add(limit.saturating_sub(1))
|
|
||||||
.checked_div(limit)
|
|
||||||
.unwrap_or(1);
|
|
||||||
let current_page = offset.checked_div(limit).unwrap_or(0);
|
|
||||||
let page_items = build_page_items(total_pages, current_page);
|
|
||||||
let pending_followers: Vec<RemoteActorData> = profile
|
let pending_followers: Vec<RemoteActorData> = profile
|
||||||
.pending_followers
|
.pending_followers
|
||||||
.iter()
|
.iter()
|
||||||
@@ -703,43 +760,44 @@ pub async fn get_user_profile_html(
|
|||||||
profile_user_id: profile_user_uuid,
|
profile_user_id: profile_user_uuid,
|
||||||
profile_url,
|
profile_url,
|
||||||
stats: &profile.stats,
|
stats: &profile.stats,
|
||||||
avg_rating_display,
|
avg_rating_display: stats_disp.avg_rating,
|
||||||
favorite_director_display,
|
favorite_director_display: stats_disp.favorite_director,
|
||||||
most_active_month_display,
|
most_active_month_display: stats_disp.most_active_month,
|
||||||
view: profile_view.as_str(),
|
view: profile_view.as_str(),
|
||||||
entries: profile.entries.as_ref(),
|
entries: profile.entries.as_ref(),
|
||||||
current_offset: offset,
|
current_offset: pag.offset,
|
||||||
has_more,
|
has_more: pag.has_more,
|
||||||
limit,
|
limit: pag.limit,
|
||||||
history: history.as_ref(),
|
history: history.as_ref(),
|
||||||
trends: profile.trends.as_ref(),
|
trends: profile.trends.as_ref(),
|
||||||
monthly_rating_rows,
|
monthly_rating_rows,
|
||||||
heatmap,
|
heatmap,
|
||||||
page_items,
|
page_items: pag.page_items,
|
||||||
sort_by: sort_by_str.to_string(),
|
sort_by: sort_by_str.to_string(),
|
||||||
});
|
});
|
||||||
let mut resp = response.into_response();
|
let mut resp = response.into_response();
|
||||||
resp.headers_mut().remove("x-frame-options");
|
resp.headers_mut().remove("x-frame-options");
|
||||||
resp
|
resp
|
||||||
} else {
|
} else {
|
||||||
|
let goals = fetch_profile_goals(&state, profile_user_uuid).await;
|
||||||
render_page(ProfileTemplate {
|
render_page(ProfileTemplate {
|
||||||
ctx: &ctx,
|
ctx: &ctx,
|
||||||
profile_display_name: display_name,
|
profile_display_name: display_name,
|
||||||
profile_user_id: profile_user_uuid,
|
profile_user_id: profile_user_uuid,
|
||||||
stats: &profile.stats,
|
stats: &profile.stats,
|
||||||
avg_rating_display,
|
avg_rating_display: stats_disp.avg_rating,
|
||||||
favorite_director_display,
|
favorite_director_display: stats_disp.favorite_director,
|
||||||
most_active_month_display,
|
most_active_month_display: stats_disp.most_active_month,
|
||||||
view: profile_view.as_str(),
|
view: profile_view.as_str(),
|
||||||
entries: profile.entries.as_ref(),
|
entries: profile.entries.as_ref(),
|
||||||
current_offset: offset,
|
current_offset: pag.offset,
|
||||||
has_more,
|
has_more: pag.has_more,
|
||||||
limit,
|
limit: pag.limit,
|
||||||
history: history.as_ref(),
|
history: history.as_ref(),
|
||||||
trends: profile.trends.as_ref(),
|
trends: profile.trends.as_ref(),
|
||||||
monthly_rating_rows,
|
monthly_rating_rows,
|
||||||
heatmap,
|
heatmap,
|
||||||
page_items,
|
page_items: pag.page_items,
|
||||||
is_own_profile,
|
is_own_profile,
|
||||||
error: params.error,
|
error: params.error,
|
||||||
following_count: profile.following_count,
|
following_count: profile.following_count,
|
||||||
@@ -747,29 +805,7 @@ pub async fn get_user_profile_html(
|
|||||||
pending_followers,
|
pending_followers,
|
||||||
sort_by: sort_by_str.to_string(),
|
sort_by: sort_by_str.to_string(),
|
||||||
search: params.search.clone(),
|
search: params.search.clone(),
|
||||||
goals: {
|
goals,
|
||||||
let goals_list = application::goals::list::execute(
|
|
||||||
&application::goals::deps::GoalQueryDeps {
|
|
||||||
goal: state.app_ctx.repos.goal.clone(),
|
|
||||||
stats: state.app_ctx.repos.stats.clone(),
|
|
||||||
},
|
|
||||||
application::goals::queries::ListGoalsQuery {
|
|
||||||
user_id: profile_user_uuid,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap_or_default();
|
|
||||||
goals_list
|
|
||||||
.iter()
|
|
||||||
.map(|g| template_askama::GoalViewData {
|
|
||||||
year: g.goal.year(),
|
|
||||||
target_count: g.goal.target_count(),
|
|
||||||
current_count: g.current_count,
|
|
||||||
percentage: g.percentage().round(),
|
|
||||||
is_complete: g.is_complete(),
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
.into_response()
|
.into_response()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ async fn wire_dependencies() -> anyhow::Result<(AppState, axum::Router)> {
|
|||||||
movie_repo: Arc::clone(&db.movie_query),
|
movie_repo: Arc::clone(&db.movie_query),
|
||||||
review_repo: Arc::clone(&db.review),
|
review_repo: Arc::clone(&db.review),
|
||||||
diary_repo: Arc::clone(&db.diary),
|
diary_repo: Arc::clone(&db.diary),
|
||||||
goal_repo: Arc::clone(&db.goal),
|
goal_repo: Arc::clone(&db.goal_query),
|
||||||
stats_repo: Arc::clone(&db.stats),
|
stats_repo: Arc::clone(&db.stats),
|
||||||
user_repo: Arc::clone(&db.user),
|
user_repo: Arc::clone(&db.user),
|
||||||
federation_settings: std::sync::Arc::clone(&db.federation_settings),
|
federation_settings: std::sync::Arc::clone(&db.federation_settings),
|
||||||
@@ -158,14 +158,15 @@ async fn wire_dependencies() -> anyhow::Result<(AppState, axum::Router)> {
|
|||||||
#[cfg(feature = "federation")]
|
#[cfg(feature = "federation")]
|
||||||
remote_watchlist: remote_watchlist_repo,
|
remote_watchlist: remote_watchlist_repo,
|
||||||
#[cfg(not(feature = "federation"))]
|
#[cfg(not(feature = "federation"))]
|
||||||
remote_watchlist: Arc::new(domain::testing::NoopRemoteWatchlistRepository),
|
remote_watchlist: Arc::new(domain::ports::noop::NoopRemoteWatchlistRepository),
|
||||||
#[cfg(feature = "federation")]
|
#[cfg(feature = "federation")]
|
||||||
social_query: social_query.clone(),
|
social_query: social_query.clone(),
|
||||||
#[cfg(not(feature = "federation"))]
|
#[cfg(not(feature = "federation"))]
|
||||||
social_query: Arc::new(domain::testing::NoopSocialQueryPort),
|
social_query: Arc::new(domain::ports::noop::NoopSocialQueryPort),
|
||||||
wrapup_stats: db.wrapup_stats,
|
wrapup_stats: db.wrapup_stats,
|
||||||
wrapup_repo: db.wrapup_repo,
|
wrapup_repo: db.wrapup_repo,
|
||||||
goal: db.goal,
|
goal_command: db.goal_command,
|
||||||
|
goal_query: db.goal_query,
|
||||||
user_settings: db.user_settings,
|
user_settings: db.user_settings,
|
||||||
remote_goal: db.remote_goal,
|
remote_goal: db.remote_goal,
|
||||||
refresh_session: db.refresh_session,
|
refresh_session: db.refresh_session,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use domain::{
|
|||||||
collections::{PageParams, Paginated},
|
collections::{PageParams, Paginated},
|
||||||
},
|
},
|
||||||
ports::{
|
ports::{
|
||||||
AuthService, DiaryRepository, EventPublisher, MetadataClient, MovieCommand, MovieQuery,
|
AuthService, DiaryQuery, EventPublisher, MetadataClient, MovieCommand, MovieQuery,
|
||||||
ObjectStorage, PasswordHasher, PersonCommand, PersonQuery, PosterFetcherClient,
|
ObjectStorage, PasswordHasher, PersonCommand, PersonQuery, PosterFetcherClient,
|
||||||
ReviewRepository, SearchCommand, SearchPort, StatsRepository, UserRepository,
|
ReviewRepository, SearchCommand, SearchPort, StatsRepository, UserRepository,
|
||||||
WatchlistRepository,
|
WatchlistRepository,
|
||||||
@@ -106,7 +106,7 @@ impl ReviewRepository for Panic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl DiaryRepository for Panic {
|
impl DiaryQuery for Panic {
|
||||||
async fn query_diary(&self, _: &DiaryFilter) -> Result<Paginated<DiaryEntry>, DomainError> {
|
async fn query_diary(&self, _: &DiaryFilter) -> Result<Paginated<DiaryEntry>, DomainError> {
|
||||||
panic!()
|
panic!()
|
||||||
}
|
}
|
||||||
@@ -682,7 +682,7 @@ impl domain::ports::WrapUpRepository for Panic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl domain::ports::GoalRepository for Panic {
|
impl domain::ports::GoalCommand for Panic {
|
||||||
async fn save(&self, _: &domain::models::Goal) -> Result<(), DomainError> {
|
async fn save(&self, _: &domain::models::Goal) -> Result<(), DomainError> {
|
||||||
panic!()
|
panic!()
|
||||||
}
|
}
|
||||||
@@ -696,6 +696,10 @@ impl domain::ports::GoalRepository for Panic {
|
|||||||
) -> Result<(), DomainError> {
|
) -> Result<(), DomainError> {
|
||||||
panic!()
|
panic!()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl domain::ports::GoalQuery for Panic {
|
||||||
async fn find_by_user_and_year(
|
async fn find_by_user_and_year(
|
||||||
&self,
|
&self,
|
||||||
_: &domain::value_objects::UserId,
|
_: &domain::value_objects::UserId,
|
||||||
@@ -810,7 +814,8 @@ pub fn make_test_state(auth_service: Arc<dyn AuthService>) -> crate::state::AppS
|
|||||||
social_query: Arc::clone(&repo) as _,
|
social_query: Arc::clone(&repo) as _,
|
||||||
wrapup_stats: Arc::clone(&repo) as _,
|
wrapup_stats: Arc::clone(&repo) as _,
|
||||||
wrapup_repo: Arc::clone(&repo) as _,
|
wrapup_repo: Arc::clone(&repo) as _,
|
||||||
goal: Arc::clone(&repo) as _,
|
goal_command: Arc::clone(&repo) as _,
|
||||||
|
goal_query: Arc::clone(&repo) as _,
|
||||||
user_settings: Arc::clone(&repo) as _,
|
user_settings: Arc::clone(&repo) as _,
|
||||||
remote_goal: Arc::clone(&repo) as _,
|
remote_goal: Arc::clone(&repo) as _,
|
||||||
refresh_session: Arc::clone(&repo) as _,
|
refresh_session: Arc::clone(&repo) as _,
|
||||||
|
|||||||
@@ -467,7 +467,8 @@ async fn test_app() -> Router {
|
|||||||
social_query: Arc::new(PanicSocialQuery),
|
social_query: Arc::new(PanicSocialQuery),
|
||||||
wrapup_stats: Arc::new(domain::testing::PanicWrapUpStatsQuery) as _,
|
wrapup_stats: Arc::new(domain::testing::PanicWrapUpStatsQuery) as _,
|
||||||
wrapup_repo: Arc::new(domain::testing::PanicWrapUpRepository) as _,
|
wrapup_repo: Arc::new(domain::testing::PanicWrapUpRepository) as _,
|
||||||
goal: Arc::new(domain::testing::NoopGoalRepository),
|
goal_command: Arc::new(domain::testing::NoopGoalCommand),
|
||||||
|
goal_query: Arc::new(domain::testing::NoopGoalQuery),
|
||||||
user_settings: Arc::new(domain::testing::NoopUserSettingsRepository),
|
user_settings: Arc::new(domain::testing::NoopUserSettingsRepository),
|
||||||
remote_goal: Arc::new(domain::testing::NoopRemoteGoalRepository),
|
remote_goal: Arc::new(domain::testing::NoopRemoteGoalRepository),
|
||||||
refresh_session: Arc::new(domain::testing::PanicRefreshSessionRepository),
|
refresh_session: Arc::new(domain::testing::PanicRefreshSessionRepository),
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ sqlite-federation = ["sqlite", "dep:sqlite-federation", "dep:activitypub", "fede
|
|||||||
postgres-federation = ["postgres", "dep:postgres-federation", "dep:activitypub", "federation"]
|
postgres-federation = ["postgres", "dep:postgres-federation", "dep:activitypub", "federation"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
domain = { workspace = true, features = ["test-helpers"] }
|
domain = { workspace = true }
|
||||||
application = { workspace = true }
|
application = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use domain::ports::{
|
use domain::ports::{
|
||||||
DiaryRepository, GoalRepository, ImageRefCommand, ImageRefQuery, ImportSessionRepository,
|
DiaryQuery, GoalCommand, GoalQuery, ImageRefCommand, ImageRefQuery, ImportSessionRepository,
|
||||||
LocalApContentQuery, MovieCommand, MovieDeduplicator, MovieProfileRepository, MovieQuery,
|
LocalApContentQuery, MovieCommand, MovieDeduplicator, MovieProfileRepository, MovieQuery,
|
||||||
PersonCommand, PersonQuery, ReviewRepository, SearchCommand, StatsRepository, UserRepository,
|
PersonCommand, PersonQuery, ReviewRepository, SearchCommand, StatsRepository, UserRepository,
|
||||||
WatchEventCommand, WatchEventQuery,
|
WatchEventCommand, WatchEventQuery,
|
||||||
@@ -14,9 +14,10 @@ pub struct WorkerDbOutput {
|
|||||||
pub movie_command: Arc<dyn MovieCommand>,
|
pub movie_command: Arc<dyn MovieCommand>,
|
||||||
pub movie_query: Arc<dyn MovieQuery>,
|
pub movie_query: Arc<dyn MovieQuery>,
|
||||||
pub review: Arc<dyn ReviewRepository>,
|
pub review: Arc<dyn ReviewRepository>,
|
||||||
pub diary: Arc<dyn DiaryRepository>,
|
pub diary: Arc<dyn DiaryQuery>,
|
||||||
pub stats: Arc<dyn StatsRepository>,
|
pub stats: Arc<dyn StatsRepository>,
|
||||||
pub goal: Arc<dyn GoalRepository>,
|
pub _goal_command: Arc<dyn GoalCommand>,
|
||||||
|
pub goal_query: Arc<dyn GoalQuery>,
|
||||||
pub user: Arc<dyn UserRepository>,
|
pub user: Arc<dyn UserRepository>,
|
||||||
pub import_session: Arc<dyn ImportSessionRepository>,
|
pub import_session: Arc<dyn ImportSessionRepository>,
|
||||||
pub movie_profile: Arc<dyn MovieProfileRepository>,
|
pub movie_profile: Arc<dyn MovieProfileRepository>,
|
||||||
@@ -55,7 +56,8 @@ pub async fn connect(database_url: &str, backend: &str) -> anyhow::Result<Worker
|
|||||||
review: w.review,
|
review: w.review,
|
||||||
diary: w.diary,
|
diary: w.diary,
|
||||||
stats: w.stats,
|
stats: w.stats,
|
||||||
goal: w.goal,
|
_goal_command: w.goal_command,
|
||||||
|
goal_query: w.goal_query,
|
||||||
user: w.user,
|
user: w.user,
|
||||||
import_session: w.import_session,
|
import_session: w.import_session,
|
||||||
movie_profile: w.movie_profile,
|
movie_profile: w.movie_profile,
|
||||||
@@ -94,7 +96,8 @@ pub async fn connect(database_url: &str, backend: &str) -> anyhow::Result<Worker
|
|||||||
review: w.review,
|
review: w.review,
|
||||||
diary: w.diary,
|
diary: w.diary,
|
||||||
stats: w.stats,
|
stats: w.stats,
|
||||||
goal: w.goal,
|
_goal_command: w.goal_command,
|
||||||
|
goal_query: w.goal_query,
|
||||||
user: w.user,
|
user: w.user,
|
||||||
import_session: w.import_session,
|
import_session: w.import_session,
|
||||||
movie_profile: w.movie_profile,
|
movie_profile: w.movie_profile,
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
Arc::clone(&db.movie_query),
|
Arc::clone(&db.movie_query),
|
||||||
Arc::clone(&db.review),
|
Arc::clone(&db.review),
|
||||||
Arc::clone(&db.diary),
|
Arc::clone(&db.diary),
|
||||||
Arc::clone(&db.goal),
|
Arc::clone(&db.goal_query),
|
||||||
Arc::clone(&db.stats),
|
Arc::clone(&db.stats),
|
||||||
Arc::clone(&db.user),
|
Arc::clone(&db.user),
|
||||||
app_config.base_url.clone(),
|
app_config.base_url.clone(),
|
||||||
|
|||||||
Reference in New Issue
Block a user