refactor(diary): DeleteReviewDeps, GetMovieSocialPageDeps, GetActivityFeedDeps
This commit is contained in:
@@ -1,16 +1,15 @@
|
|||||||
use crate::{context::AppContext, diary::commands::DeleteReviewCommand};
|
use crate::diary::{commands::DeleteReviewCommand, deps::DeleteReviewDeps};
|
||||||
use domain::{
|
use domain::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
events::DomainEvent,
|
events::DomainEvent,
|
||||||
value_objects::{ReviewId, UserId},
|
value_objects::{ReviewId, UserId},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn execute(ctx: &AppContext, cmd: DeleteReviewCommand) -> Result<(), DomainError> {
|
pub async fn execute(deps: &DeleteReviewDeps, cmd: DeleteReviewCommand) -> Result<(), DomainError> {
|
||||||
let review_id = ReviewId::from_uuid(cmd.review_id);
|
let review_id = ReviewId::from_uuid(cmd.review_id);
|
||||||
let requesting_user_id = UserId::from_uuid(cmd.requesting_user_id);
|
let requesting_user_id = UserId::from_uuid(cmd.requesting_user_id);
|
||||||
|
|
||||||
let review = ctx
|
let review = deps
|
||||||
.repos
|
|
||||||
.review
|
.review
|
||||||
.get_review_by_id(&review_id)
|
.get_review_by_id(&review_id)
|
||||||
.await?
|
.await?
|
||||||
@@ -21,10 +20,9 @@ pub async fn execute(ctx: &AppContext, cmd: DeleteReviewCommand) -> Result<(), D
|
|||||||
}
|
}
|
||||||
|
|
||||||
let movie_id = review.movie_id().clone();
|
let movie_id = review.movie_id().clone();
|
||||||
ctx.repos.review.delete_review(&review_id).await?;
|
deps.review.delete_review(&review_id).await?;
|
||||||
|
|
||||||
if let Err(e) = ctx
|
if let Err(e) = deps
|
||||||
.services
|
|
||||||
.event_publisher
|
.event_publisher
|
||||||
.publish(&DomainEvent::ReviewDeleted {
|
.publish(&DomainEvent::ReviewDeleted {
|
||||||
review_id: review_id.clone(),
|
review_id: review_id.clone(),
|
||||||
@@ -35,13 +33,12 @@ pub async fn execute(ctx: &AppContext, cmd: DeleteReviewCommand) -> Result<(), D
|
|||||||
tracing::warn!("failed to publish ReviewDeleted: {e}");
|
tracing::warn!("failed to publish ReviewDeleted: {e}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let history = ctx.repos.diary.get_review_history(&movie_id).await?;
|
let history = deps.diary.get_review_history(&movie_id).await?;
|
||||||
if history.viewings().is_empty() {
|
if history.viewings().is_empty() {
|
||||||
let poster_path = history.movie().poster_path().cloned();
|
let poster_path = history.movie().poster_path().cloned();
|
||||||
ctx.repos.movie.delete_movie(&movie_id).await?;
|
deps.movie.delete_movie(&movie_id).await?;
|
||||||
// best-effort: movie is already deleted, so publish failure is non-fatal
|
// best-effort: movie is already deleted, so publish failure is non-fatal
|
||||||
if let Err(e) = ctx
|
if let Err(e) = deps
|
||||||
.services
|
|
||||||
.event_publisher
|
.event_publisher
|
||||||
.publish(&DomainEvent::MovieDeleted {
|
.publish(&DomainEvent::MovieDeleted {
|
||||||
movie_id,
|
movie_id,
|
||||||
|
|||||||
27
crates/application/src/diary/deps.rs
Normal file
27
crates/application/src/diary/deps.rs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use domain::ports::{
|
||||||
|
DiaryRepository, EventPublisher, MovieProfileRepository, MovieRepository, ReviewRepository,
|
||||||
|
SocialQueryPort,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::config::AppConfig;
|
||||||
|
|
||||||
|
pub struct DeleteReviewDeps {
|
||||||
|
pub review: Arc<dyn ReviewRepository>,
|
||||||
|
pub diary: Arc<dyn DiaryRepository>,
|
||||||
|
pub movie: Arc<dyn MovieRepository>,
|
||||||
|
pub event_publisher: Arc<dyn EventPublisher>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GetMovieSocialPageDeps {
|
||||||
|
pub movie: Arc<dyn MovieRepository>,
|
||||||
|
pub diary: Arc<dyn DiaryRepository>,
|
||||||
|
pub movie_profile: Arc<dyn MovieProfileRepository>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GetActivityFeedDeps {
|
||||||
|
pub diary: Arc<dyn DiaryRepository>,
|
||||||
|
pub social_query: Arc<dyn SocialQueryPort>,
|
||||||
|
pub config: AppConfig,
|
||||||
|
}
|
||||||
@@ -1,15 +1,20 @@
|
|||||||
use domain::{errors::DomainError, value_objects::UserId};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::{context::AppContext, diary::queries::ExportQuery};
|
use domain::{
|
||||||
|
errors::DomainError,
|
||||||
|
ports::{DiaryExporter, DiaryRepository},
|
||||||
|
value_objects::UserId,
|
||||||
|
};
|
||||||
|
|
||||||
pub async fn execute(ctx: &AppContext, query: ExportQuery) -> Result<Vec<u8>, DomainError> {
|
use crate::diary::queries::ExportQuery;
|
||||||
let entries = ctx
|
|
||||||
.repos
|
pub async fn execute(
|
||||||
.diary
|
diary: &Arc<dyn DiaryRepository>,
|
||||||
|
diary_exporter: &Arc<dyn DiaryExporter>,
|
||||||
|
query: ExportQuery,
|
||||||
|
) -> Result<Vec<u8>, DomainError> {
|
||||||
|
let entries = diary
|
||||||
.get_user_history(&UserId::from_uuid(query.user_id))
|
.get_user_history(&UserId::from_uuid(query.user_id))
|
||||||
.await?;
|
.await?;
|
||||||
ctx.services
|
diary_exporter.serialize_entries(&entries, query.format).await
|
||||||
.diary_exporter
|
|
||||||
.serialize_entries(&entries, query.format)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{context::AppContext, diary::queries::GetActivityFeedQuery};
|
use crate::diary::{deps::GetActivityFeedDeps, queries::GetActivityFeedQuery};
|
||||||
use domain::{
|
use domain::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
models::{
|
models::{
|
||||||
@@ -9,15 +9,14 @@ use domain::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub async fn execute(
|
pub async fn execute(
|
||||||
ctx: &AppContext,
|
deps: &GetActivityFeedDeps,
|
||||||
query: GetActivityFeedQuery,
|
query: GetActivityFeedQuery,
|
||||||
) -> Result<Paginated<FeedEntry>, DomainError> {
|
) -> Result<Paginated<FeedEntry>, DomainError> {
|
||||||
let page = PageParams::new(Some(query.limit), Some(query.offset))?;
|
let page = PageParams::new(Some(query.limit), Some(query.offset))?;
|
||||||
|
|
||||||
let following = build_following_filter(ctx, &query).await;
|
let following = build_following_filter(deps, &query).await;
|
||||||
|
|
||||||
ctx.repos
|
deps.diary
|
||||||
.diary
|
|
||||||
.query_activity_feed_filtered(
|
.query_activity_feed_filtered(
|
||||||
&page,
|
&page,
|
||||||
&query.sort_by,
|
&query.sort_by,
|
||||||
@@ -28,15 +27,14 @@ pub async fn execute(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn build_following_filter(
|
async fn build_following_filter(
|
||||||
ctx: &AppContext,
|
deps: &GetActivityFeedDeps,
|
||||||
query: &GetActivityFeedQuery,
|
query: &GetActivityFeedQuery,
|
||||||
) -> Option<FollowingFilter> {
|
) -> Option<FollowingFilter> {
|
||||||
if !query.filter_following {
|
if !query.filter_following {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let viewer_id = query.viewer_user_id?;
|
let viewer_id = query.viewer_user_id?;
|
||||||
let urls = ctx
|
let urls = deps
|
||||||
.repos
|
|
||||||
.social_query
|
.social_query
|
||||||
.get_accepted_following_urls(viewer_id)
|
.get_accepted_following_urls(viewer_id)
|
||||||
.await
|
.await
|
||||||
@@ -47,7 +45,7 @@ async fn build_following_filter(
|
|||||||
remote_actor_urls: vec![],
|
remote_actor_urls: vec![],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let base_url = &ctx.config.base_url;
|
let base_url = &deps.config.base_url;
|
||||||
let mut local_ids = vec![viewer_id];
|
let mut local_ids = vec![viewer_id];
|
||||||
let mut remote_urls = Vec::new();
|
let mut remote_urls = Vec::new();
|
||||||
for url in urls {
|
for url in urls {
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use domain::{
|
use domain::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
models::{
|
models::{
|
||||||
DiaryEntry, DiaryFilter, SortDirection,
|
DiaryEntry, DiaryFilter, SortDirection,
|
||||||
collections::{PageParams, Paginated},
|
collections::{PageParams, Paginated},
|
||||||
},
|
},
|
||||||
|
ports::DiaryRepository,
|
||||||
value_objects::{MovieId, UserId},
|
value_objects::{MovieId, UserId},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{context::AppContext, diary::queries::GetDiaryQuery};
|
use crate::diary::queries::GetDiaryQuery;
|
||||||
|
|
||||||
pub async fn execute(
|
pub async fn execute(
|
||||||
ctx: &AppContext,
|
diary: &Arc<dyn DiaryRepository>,
|
||||||
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)?;
|
||||||
@@ -25,7 +28,7 @@ pub async fn execute(
|
|||||||
search: None,
|
search: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.repos.diary.query_diary(&filter).await
|
diary.query_diary(&filter).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use domain::{
|
|||||||
value_objects::MovieId,
|
value_objects::MovieId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{context::AppContext, diary::queries::GetMovieSocialPageQuery};
|
use crate::diary::{deps::GetMovieSocialPageDeps, queries::GetMovieSocialPageQuery};
|
||||||
|
|
||||||
pub struct MovieSocialPageResult {
|
pub struct MovieSocialPageResult {
|
||||||
pub movie: Movie,
|
pub movie: Movie,
|
||||||
@@ -17,23 +17,22 @@ pub struct MovieSocialPageResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn execute(
|
pub async fn execute(
|
||||||
ctx: &AppContext,
|
deps: &GetMovieSocialPageDeps,
|
||||||
query: GetMovieSocialPageQuery,
|
query: GetMovieSocialPageQuery,
|
||||||
) -> Result<MovieSocialPageResult, DomainError> {
|
) -> Result<MovieSocialPageResult, DomainError> {
|
||||||
let movie_id = MovieId::from_uuid(query.movie_id);
|
let movie_id = MovieId::from_uuid(query.movie_id);
|
||||||
let page = PageParams::new(Some(query.limit), Some(query.offset))?;
|
let page = PageParams::new(Some(query.limit), Some(query.offset))?;
|
||||||
|
|
||||||
let movie = ctx
|
let movie = deps
|
||||||
.repos
|
|
||||||
.movie
|
.movie
|
||||||
.get_movie_by_id(&movie_id)
|
.get_movie_by_id(&movie_id)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| DomainError::NotFound(format!("Movie {}", query.movie_id)))?;
|
.ok_or_else(|| DomainError::NotFound(format!("Movie {}", query.movie_id)))?;
|
||||||
|
|
||||||
let (stats, reviews, profile) = tokio::try_join!(
|
let (stats, reviews, profile) = tokio::try_join!(
|
||||||
ctx.repos.diary.get_movie_stats(&movie_id),
|
deps.diary.get_movie_stats(&movie_id),
|
||||||
ctx.repos.diary.get_movie_social_feed(&movie_id, &page),
|
deps.diary.get_movie_social_feed(&movie_id, &page),
|
||||||
ctx.repos.movie_profile.get_by_movie_id(&movie_id),
|
deps.movie_profile.get_by_movie_id(&movie_id),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(MovieSocialPageResult {
|
Ok(MovieSocialPageResult {
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use domain::{
|
use domain::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
models::ReviewHistory,
|
models::ReviewHistory,
|
||||||
|
ports::DiaryRepository,
|
||||||
services::review_history::{ReviewHistoryAnalyzer, Trend},
|
services::review_history::{ReviewHistoryAnalyzer, Trend},
|
||||||
value_objects::MovieId,
|
value_objects::MovieId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{context::AppContext, diary::queries::GetReviewHistoryQuery};
|
use crate::diary::queries::GetReviewHistoryQuery;
|
||||||
|
|
||||||
pub async fn execute(
|
pub async fn execute(
|
||||||
ctx: &AppContext,
|
diary: &Arc<dyn DiaryRepository>,
|
||||||
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);
|
||||||
|
|
||||||
let mut history = ctx.repos.diary.get_review_history(&movie_id).await?;
|
let mut history = diary.get_review_history(&movie_id).await?;
|
||||||
|
|
||||||
let trend = ReviewHistoryAnalyzer::rating_trend(&history)?;
|
let trend = ReviewHistoryAnalyzer::rating_trend(&history)?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use domain::errors::DomainError;
|
use domain::errors::DomainError;
|
||||||
|
|
||||||
use crate::{context::AppContext, diary::commands::LogReviewCommand};
|
use crate::{diary::commands::LogReviewCommand, ports::ReviewLogger};
|
||||||
|
|
||||||
pub async fn execute(ctx: &AppContext, cmd: LogReviewCommand) -> Result<(), DomainError> {
|
pub async fn execute(
|
||||||
ctx.services.review_logger.log_review(cmd).await
|
review_logger: &Arc<dyn ReviewLogger>,
|
||||||
|
cmd: LogReviewCommand,
|
||||||
|
) -> Result<(), DomainError> {
|
||||||
|
review_logger.log_review(cmd).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod delete_review;
|
pub mod delete_review;
|
||||||
|
pub mod deps;
|
||||||
pub mod export_diary;
|
pub mod export_diary;
|
||||||
pub mod get_activity_feed;
|
pub mod get_activity_feed;
|
||||||
pub mod get_diary;
|
pub mod get_diary;
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ use domain::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diary::commands::DeleteReviewCommand, diary::delete_review, test_helpers::TestContextBuilder,
|
diary::commands::DeleteReviewCommand,
|
||||||
|
diary::delete_review,
|
||||||
|
diary::deps::DeleteReviewDeps,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn make_movie() -> Movie {
|
fn make_movie() -> Movie {
|
||||||
@@ -51,15 +53,15 @@ async fn test_delete_review_removes_it() {
|
|||||||
reviews.save_review(&review).await.unwrap();
|
reviews.save_review(&review).await.unwrap();
|
||||||
diary.seed_history(movie.clone(), vec![]);
|
diary.seed_history(movie.clone(), vec![]);
|
||||||
|
|
||||||
let ctx = TestContextBuilder::new()
|
let deps = DeleteReviewDeps {
|
||||||
.with_movies(Arc::clone(&movies) as _)
|
review: Arc::clone(&reviews) as _,
|
||||||
.with_reviews(Arc::clone(&reviews) as _)
|
diary: diary.clone() as _,
|
||||||
.with_diary(Arc::clone(&diary) as _)
|
movie: Arc::clone(&movies) as _,
|
||||||
.with_event_publisher(Arc::clone(&events) as _)
|
event_publisher: Arc::clone(&events) as _,
|
||||||
.build();
|
};
|
||||||
|
|
||||||
delete_review::execute(
|
delete_review::execute(
|
||||||
&ctx,
|
&deps,
|
||||||
DeleteReviewCommand {
|
DeleteReviewCommand {
|
||||||
review_id: review.id().value(),
|
review_id: review.id().value(),
|
||||||
requesting_user_id: user_id.value(),
|
requesting_user_id: user_id.value(),
|
||||||
@@ -78,6 +80,9 @@ 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 movies = InMemoryMovieRepository::new();
|
||||||
|
let events = NoopEventPublisher::new();
|
||||||
|
|
||||||
let movie_id = MovieId::from_uuid(uuid::Uuid::new_v4());
|
let movie_id = MovieId::from_uuid(uuid::Uuid::new_v4());
|
||||||
let owner_id = UserId::from_uuid(uuid::Uuid::new_v4());
|
let owner_id = UserId::from_uuid(uuid::Uuid::new_v4());
|
||||||
@@ -86,12 +91,15 @@ async fn test_delete_review_wrong_user_is_unauthorized() {
|
|||||||
|
|
||||||
reviews.save_review(&review).await.unwrap();
|
reviews.save_review(&review).await.unwrap();
|
||||||
|
|
||||||
let ctx = TestContextBuilder::new()
|
let deps = DeleteReviewDeps {
|
||||||
.with_reviews(Arc::clone(&reviews) as _)
|
review: Arc::clone(&reviews) as _,
|
||||||
.build();
|
diary: diary as _,
|
||||||
|
movie: movies as _,
|
||||||
|
event_publisher: Arc::clone(&events) as _,
|
||||||
|
};
|
||||||
|
|
||||||
let result = delete_review::execute(
|
let result = delete_review::execute(
|
||||||
&ctx,
|
&deps,
|
||||||
DeleteReviewCommand {
|
DeleteReviewCommand {
|
||||||
review_id: review.id().value(),
|
review_id: review.id().value(),
|
||||||
requesting_user_id: other_id,
|
requesting_user_id: other_id,
|
||||||
|
|||||||
@@ -2,18 +2,30 @@ 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 crate::{
|
use crate::{
|
||||||
diary::get_activity_feed, diary::queries::GetActivityFeedQuery,
|
config::AppConfig,
|
||||||
|
diary::deps::GetActivityFeedDeps,
|
||||||
|
diary::get_activity_feed,
|
||||||
|
diary::queries::GetActivityFeedQuery,
|
||||||
test_helpers::TestContextBuilder,
|
test_helpers::TestContextBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn default_deps() -> GetActivityFeedDeps {
|
||||||
|
GetActivityFeedDeps {
|
||||||
|
diary: FakeDiaryRepository::new() as _,
|
||||||
|
social_query: Arc::new(NoopSocialQueryPort),
|
||||||
|
config: TestContextBuilder::new().config,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn returns_empty_feed() {
|
async fn returns_empty_feed() {
|
||||||
let ctx = TestContextBuilder::new().build();
|
let deps = default_deps();
|
||||||
|
|
||||||
let result = get_activity_feed::execute(
|
let result = get_activity_feed::execute(
|
||||||
&ctx,
|
&deps,
|
||||||
GetActivityFeedQuery {
|
GetActivityFeedQuery {
|
||||||
limit: 10,
|
limit: 10,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
@@ -32,12 +44,12 @@ async fn returns_empty_feed() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn returns_feed_with_following_filter() {
|
async fn returns_feed_with_following_filter() {
|
||||||
let ctx = TestContextBuilder::new().build();
|
let deps = default_deps();
|
||||||
|
|
||||||
let viewer = uuid::Uuid::new_v4();
|
let viewer = uuid::Uuid::new_v4();
|
||||||
|
|
||||||
let result = get_activity_feed::execute(
|
let result = get_activity_feed::execute(
|
||||||
&ctx,
|
&deps,
|
||||||
GetActivityFeedQuery {
|
GetActivityFeedQuery {
|
||||||
limit: 10,
|
limit: 10,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
@@ -93,12 +105,24 @@ async fn following_filter_parses_local_and_remote_urls() {
|
|||||||
|
|
||||||
let social = Arc::new(FakeSocialWithFollowing(following_urls));
|
let social = Arc::new(FakeSocialWithFollowing(following_urls));
|
||||||
|
|
||||||
let ctx = TestContextBuilder::new()
|
let deps = GetActivityFeedDeps {
|
||||||
.with_social_query(social as _)
|
diary: FakeDiaryRepository::new() as _,
|
||||||
.build();
|
social_query: social as _,
|
||||||
|
config: AppConfig {
|
||||||
|
allow_registration: true,
|
||||||
|
base_url: "http://localhost:3000".into(),
|
||||||
|
rate_limit: 20,
|
||||||
|
refresh_ttl_seconds: 2_592_000,
|
||||||
|
wrapup: crate::config::WrapUpConfig {
|
||||||
|
font_path: None,
|
||||||
|
logo_path: None,
|
||||||
|
bg_dir: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
let result = get_activity_feed::execute(
|
let result = get_activity_feed::execute(
|
||||||
&ctx,
|
&deps,
|
||||||
GetActivityFeedQuery {
|
GetActivityFeedQuery {
|
||||||
limit: 10,
|
limit: 10,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
@@ -118,10 +142,10 @@ async fn following_filter_parses_local_and_remote_urls() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn following_filter_without_viewer_returns_none() {
|
async fn following_filter_without_viewer_returns_none() {
|
||||||
let ctx = TestContextBuilder::new().build();
|
let deps = default_deps();
|
||||||
|
|
||||||
let result = get_activity_feed::execute(
|
let result = get_activity_feed::execute(
|
||||||
&ctx,
|
&deps,
|
||||||
GetActivityFeedQuery {
|
GetActivityFeedQuery {
|
||||||
limit: 10,
|
limit: 10,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
use crate::{diary::get_diary, diary::queries::GetDiaryQuery, test_helpers::TestContextBuilder};
|
use domain::testing::FakeDiaryRepository;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::{diary::get_diary, diary::queries::GetDiaryQuery};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn returns_empty_page() {
|
async fn returns_empty_page() {
|
||||||
let ctx = TestContextBuilder::new().build();
|
let diary = FakeDiaryRepository::new() as Arc<dyn domain::ports::DiaryRepository>;
|
||||||
|
|
||||||
let result = get_diary::execute(
|
let result = get_diary::execute(
|
||||||
&ctx,
|
&diary,
|
||||||
GetDiaryQuery {
|
GetDiaryQuery {
|
||||||
limit: None,
|
limit: None,
|
||||||
offset: None,
|
offset: None,
|
||||||
|
|||||||
@@ -5,21 +5,26 @@ use uuid::Uuid;
|
|||||||
use domain::{
|
use domain::{
|
||||||
models::Movie,
|
models::Movie,
|
||||||
ports::MovieRepository,
|
ports::MovieRepository,
|
||||||
testing::InMemoryMovieRepository,
|
testing::{FakeDiaryRepository, InMemoryMovieProfileRepository, InMemoryMovieRepository},
|
||||||
value_objects::{MovieTitle, ReleaseYear},
|
value_objects::{MovieTitle, ReleaseYear},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
diary::get_movie_social_page, diary::queries::GetMovieSocialPageQuery,
|
diary::deps::GetMovieSocialPageDeps,
|
||||||
test_helpers::TestContextBuilder,
|
diary::get_movie_social_page,
|
||||||
|
diary::queries::GetMovieSocialPageQuery,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fails_when_movie_not_found() {
|
async fn fails_when_movie_not_found() {
|
||||||
let ctx = TestContextBuilder::new().build();
|
let deps = GetMovieSocialPageDeps {
|
||||||
|
movie: InMemoryMovieRepository::new(),
|
||||||
|
diary: FakeDiaryRepository::new() as _,
|
||||||
|
movie_profile: InMemoryMovieProfileRepository::new(),
|
||||||
|
};
|
||||||
|
|
||||||
let result = get_movie_social_page::execute(
|
let result = get_movie_social_page::execute(
|
||||||
&ctx,
|
&deps,
|
||||||
GetMovieSocialPageQuery {
|
GetMovieSocialPageQuery {
|
||||||
movie_id: Uuid::new_v4(),
|
movie_id: Uuid::new_v4(),
|
||||||
limit: 10,
|
limit: 10,
|
||||||
@@ -45,12 +50,14 @@ async fn returns_movie_social_page() {
|
|||||||
let movie_uuid = movie.id().value();
|
let movie_uuid = movie.id().value();
|
||||||
movies.upsert_movie(&movie).await.unwrap();
|
movies.upsert_movie(&movie).await.unwrap();
|
||||||
|
|
||||||
let ctx = TestContextBuilder::new()
|
let deps = GetMovieSocialPageDeps {
|
||||||
.with_movies(Arc::clone(&movies) as _)
|
movie: Arc::clone(&movies) as _,
|
||||||
.build();
|
diary: FakeDiaryRepository::new() as _,
|
||||||
|
movie_profile: InMemoryMovieProfileRepository::new(),
|
||||||
|
};
|
||||||
|
|
||||||
let result = get_movie_social_page::execute(
|
let result = get_movie_social_page::execute(
|
||||||
&ctx,
|
&deps,
|
||||||
GetMovieSocialPageQuery {
|
GetMovieSocialPageQuery {
|
||||||
movie_id: movie_uuid,
|
movie_id: movie_uuid,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use domain::{
|
use domain::{
|
||||||
models::Movie,
|
models::Movie,
|
||||||
|
ports::DiaryRepository,
|
||||||
services::review_history::Trend,
|
services::review_history::Trend,
|
||||||
value_objects::{MovieTitle, ReleaseYear},
|
value_objects::{MovieTitle, ReleaseYear},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{diary::get_review_history, diary::queries::GetReviewHistoryQuery};
|
||||||
diary::get_review_history, diary::queries::GetReviewHistoryQuery,
|
|
||||||
test_helpers::TestContextBuilder,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn returns_empty_history() {
|
async fn returns_empty_history() {
|
||||||
@@ -22,10 +22,9 @@ async fn returns_empty_history() {
|
|||||||
|
|
||||||
let diary = domain::testing::FakeDiaryRepository::new();
|
let diary = domain::testing::FakeDiaryRepository::new();
|
||||||
diary.seed_history(movie, vec![]);
|
diary.seed_history(movie, vec![]);
|
||||||
|
let diary: Arc<dyn DiaryRepository> = diary;
|
||||||
|
|
||||||
let ctx = TestContextBuilder::new().with_diary(diary as _).build();
|
let (history, trend) = get_review_history::execute(&diary, GetReviewHistoryQuery { movie_id })
|
||||||
|
|
||||||
let (history, trend) = get_review_history::execute(&ctx, GetReviewHistoryQuery { movie_id })
|
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -17,24 +17,18 @@ use crate::{
|
|||||||
test_helpers::TestContextBuilder,
|
test_helpers::TestContextBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn build_ctx_with_real_logger(
|
fn build_logger(
|
||||||
movies: &Arc<InMemoryMovieRepository>,
|
movies: &Arc<InMemoryMovieRepository>,
|
||||||
reviews: &Arc<InMemoryReviewRepository>,
|
reviews: &Arc<InMemoryReviewRepository>,
|
||||||
events: &Arc<NoopEventPublisher>,
|
events: &Arc<NoopEventPublisher>,
|
||||||
) -> crate::context::AppContext {
|
) -> Arc<dyn crate::ports::ReviewLogger> {
|
||||||
let logger = Arc::new(DefaultReviewLogger::new(
|
Arc::new(DefaultReviewLogger::new(
|
||||||
Arc::clone(movies) as _,
|
Arc::clone(movies) as _,
|
||||||
Arc::clone(reviews) as _,
|
Arc::clone(reviews) as _,
|
||||||
crate::test_helpers::TestContextBuilder::new().watchlist_repo,
|
TestContextBuilder::new().watchlist_repo,
|
||||||
Arc::new(domain::testing::FakeMetadataClient) as _,
|
Arc::new(domain::testing::FakeMetadataClient) as _,
|
||||||
Arc::clone(events) as _,
|
Arc::clone(events) as _,
|
||||||
));
|
))
|
||||||
TestContextBuilder::new()
|
|
||||||
.with_movies(Arc::clone(movies) as _)
|
|
||||||
.with_reviews(Arc::clone(reviews) as _)
|
|
||||||
.with_event_publisher(Arc::clone(events) as _)
|
|
||||||
.with_review_logger(logger)
|
|
||||||
.build()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn movie_input_manual(title: &str, year: u16) -> MovieInput {
|
fn movie_input_manual(title: &str, year: u16) -> MovieInput {
|
||||||
@@ -62,7 +56,7 @@ async fn test_log_review_creates_movie_and_review() {
|
|||||||
let movies = InMemoryMovieRepository::new();
|
let movies = InMemoryMovieRepository::new();
|
||||||
let reviews = InMemoryReviewRepository::new();
|
let reviews = InMemoryReviewRepository::new();
|
||||||
let events = NoopEventPublisher::new();
|
let events = NoopEventPublisher::new();
|
||||||
let ctx = build_ctx_with_real_logger(&movies, &reviews, &events);
|
let logger = build_logger(&movies, &reviews, &events);
|
||||||
|
|
||||||
let user_id = uuid::Uuid::new_v4();
|
let user_id = uuid::Uuid::new_v4();
|
||||||
let cmd = LogReviewCommand {
|
let cmd = LogReviewCommand {
|
||||||
@@ -73,7 +67,7 @@ async fn test_log_review_creates_movie_and_review() {
|
|||||||
watched_at: Utc::now().naive_utc(),
|
watched_at: Utc::now().naive_utc(),
|
||||||
};
|
};
|
||||||
|
|
||||||
log_review::execute(&ctx, cmd).await.unwrap();
|
log_review::execute(&logger, cmd).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(reviews.count(), 1, "review should be saved");
|
assert_eq!(reviews.count(), 1, "review should be saved");
|
||||||
assert!(!events.published().is_empty(), "events should be published");
|
assert!(!events.published().is_empty(), "events should be published");
|
||||||
@@ -95,7 +89,7 @@ async fn test_log_review_reuses_existing_movie() {
|
|||||||
movies.upsert_movie(&existing_movie).await.unwrap();
|
movies.upsert_movie(&existing_movie).await.unwrap();
|
||||||
|
|
||||||
let events = NoopEventPublisher::new();
|
let events = NoopEventPublisher::new();
|
||||||
let ctx = build_ctx_with_real_logger(&movies, &reviews, &events);
|
let logger = build_logger(&movies, &reviews, &events);
|
||||||
|
|
||||||
let cmd = LogReviewCommand {
|
let cmd = LogReviewCommand {
|
||||||
user_id: uuid::Uuid::new_v4(),
|
user_id: uuid::Uuid::new_v4(),
|
||||||
@@ -105,7 +99,7 @@ async fn test_log_review_reuses_existing_movie() {
|
|||||||
watched_at: Utc::now().naive_utc(),
|
watched_at: Utc::now().naive_utc(),
|
||||||
};
|
};
|
||||||
|
|
||||||
log_review::execute(&ctx, cmd).await.unwrap();
|
log_review::execute(&logger, cmd).await.unwrap();
|
||||||
|
|
||||||
assert_eq!(movies.count(), 1, "no duplicate movie");
|
assert_eq!(movies.count(), 1, "no duplicate movie");
|
||||||
assert_eq!(reviews.count(), 1);
|
assert_eq!(reviews.count(), 1);
|
||||||
@@ -116,7 +110,8 @@ async fn test_log_review_with_invalid_rating_fails() {
|
|||||||
let movies = InMemoryMovieRepository::new();
|
let movies = InMemoryMovieRepository::new();
|
||||||
let reviews = InMemoryReviewRepository::new();
|
let reviews = InMemoryReviewRepository::new();
|
||||||
let events = NoopEventPublisher::new();
|
let events = NoopEventPublisher::new();
|
||||||
let ctx = build_ctx_with_real_logger(&movies, &reviews, &events);
|
let logger = build_logger(&movies, &reviews, &events);
|
||||||
|
|
||||||
let cmd = LogReviewCommand {
|
let cmd = LogReviewCommand {
|
||||||
user_id: uuid::Uuid::new_v4(),
|
user_id: uuid::Uuid::new_v4(),
|
||||||
input: movie_input_manual("Some Film", 2000),
|
input: movie_input_manual("Some Film", 2000),
|
||||||
@@ -124,6 +119,6 @@ async fn test_log_review_with_invalid_rating_fails() {
|
|||||||
comment: None,
|
comment: None,
|
||||||
watched_at: Utc::now().naive_utc(),
|
watched_at: Utc::now().naive_utc(),
|
||||||
};
|
};
|
||||||
let result = log_review::execute(&ctx, cmd).await;
|
let result = log_review::execute(&logger, cmd).await;
|
||||||
assert!(result.is_err(), "rating > 5 should fail");
|
assert!(result.is_err(), "rating > 5 should fail");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use application::diary::{
|
|||||||
delete_review, export_diary as export_diary_uc, get_activity_feed as get_feed_uc, get_diary,
|
delete_review, export_diary as export_diary_uc, get_activity_feed as get_feed_uc, get_diary,
|
||||||
log_review,
|
log_review,
|
||||||
queries::{ExportQuery, GetActivityFeedQuery},
|
queries::{ExportQuery, GetActivityFeedQuery},
|
||||||
|
deps::{DeleteReviewDeps, GetActivityFeedDeps},
|
||||||
};
|
};
|
||||||
use domain::models::ExportFormat;
|
use domain::models::ExportFormat;
|
||||||
|
|
||||||
@@ -50,7 +51,7 @@ pub async fn get_diary(
|
|||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(params): Query<DiaryQueryParams>,
|
Query(params): Query<DiaryQueryParams>,
|
||||||
) -> Result<Json<DiaryResponse>, ApiError> {
|
) -> Result<Json<DiaryResponse>, ApiError> {
|
||||||
let page = get_diary::execute(&state.app_ctx, to_diary_query(params)).await?;
|
let page = get_diary::execute(&state.app_ctx.repos.diary, to_diary_query(params)).await?;
|
||||||
|
|
||||||
Ok(Json(DiaryResponse {
|
Ok(Json(DiaryResponse {
|
||||||
items: page
|
items: page
|
||||||
@@ -80,7 +81,7 @@ pub async fn post_review(
|
|||||||
Json(req): Json<LogReviewRequest>,
|
Json(req): Json<LogReviewRequest>,
|
||||||
) -> Result<impl IntoResponse, ApiError> {
|
) -> Result<impl IntoResponse, ApiError> {
|
||||||
let data = LogReviewData::try_from(req).map_err(ApiError)?;
|
let data = LogReviewData::try_from(req).map_err(ApiError)?;
|
||||||
log_review::execute(&state.app_ctx, data.into_command(user.0.value())).await?;
|
log_review::execute(&state.app_ctx.services.review_logger, data.into_command(user.0.value())).await?;
|
||||||
Ok(StatusCode::CREATED)
|
Ok(StatusCode::CREATED)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +105,13 @@ pub async fn delete_review(
|
|||||||
review_id,
|
review_id,
|
||||||
requesting_user_id: user_id.value(),
|
requesting_user_id: user_id.value(),
|
||||||
};
|
};
|
||||||
delete_review::execute(&state.app_ctx, cmd).await?;
|
let deps = DeleteReviewDeps {
|
||||||
|
review: state.app_ctx.repos.review.clone(),
|
||||||
|
diary: state.app_ctx.repos.diary.clone(),
|
||||||
|
movie: state.app_ctx.repos.movie.clone(),
|
||||||
|
event_publisher: state.app_ctx.services.event_publisher.clone(),
|
||||||
|
};
|
||||||
|
delete_review::execute(&deps, cmd).await?;
|
||||||
Ok(StatusCode::NO_CONTENT)
|
Ok(StatusCode::NO_CONTENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,7 +143,13 @@ pub async fn export_diary(
|
|||||||
user_id: user.0.value(),
|
user_id: user.0.value(),
|
||||||
format,
|
format,
|
||||||
};
|
};
|
||||||
match export_diary_uc::execute(&state.app_ctx, query).await {
|
match export_diary_uc::execute(
|
||||||
|
&state.app_ctx.repos.diary,
|
||||||
|
&state.app_ctx.services.diary_exporter,
|
||||||
|
query,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(bytes) => (
|
Ok(bytes) => (
|
||||||
StatusCode::OK,
|
StatusCode::OK,
|
||||||
[
|
[
|
||||||
@@ -165,8 +178,13 @@ pub async fn get_activity_feed(
|
|||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Query(params): Query<ActivityFeedQueryParams>,
|
Query(params): Query<ActivityFeedQueryParams>,
|
||||||
) -> Result<Json<ActivityFeedResponse>, ApiError> {
|
) -> Result<Json<ActivityFeedResponse>, ApiError> {
|
||||||
|
let deps = GetActivityFeedDeps {
|
||||||
|
diary: state.app_ctx.repos.diary.clone(),
|
||||||
|
social_query: state.app_ctx.repos.social_query.clone(),
|
||||||
|
config: state.app_ctx.config.clone(),
|
||||||
|
};
|
||||||
let page = get_feed_uc::execute(
|
let page = get_feed_uc::execute(
|
||||||
&state.app_ctx,
|
&deps,
|
||||||
GetActivityFeedQuery {
|
GetActivityFeedQuery {
|
||||||
limit: params.limit.unwrap_or(20),
|
limit: params.limit.unwrap_or(20),
|
||||||
offset: params.offset.unwrap_or(0),
|
offset: params.offset.unwrap_or(0),
|
||||||
@@ -226,7 +244,7 @@ pub async fn post_review_html(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match log_review::execute(&state.app_ctx, data.into_command(user_id.value())).await {
|
match log_review::execute(&state.app_ctx.services.review_logger, data.into_command(user_id.value())).await {
|
||||||
Ok(_) => Redirect::to("/").into_response(),
|
Ok(_) => Redirect::to("/").into_response(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let msg = encode_error(&e.to_string());
|
let msg = encode_error(&e.to_string());
|
||||||
@@ -249,7 +267,13 @@ pub async fn post_delete_review_html(
|
|||||||
review_id,
|
review_id,
|
||||||
requesting_user_id: user_id.value(),
|
requesting_user_id: user_id.value(),
|
||||||
};
|
};
|
||||||
match delete_review::execute(&state.app_ctx, cmd).await {
|
let deps = DeleteReviewDeps {
|
||||||
|
review: state.app_ctx.repos.review.clone(),
|
||||||
|
diary: state.app_ctx.repos.diary.clone(),
|
||||||
|
movie: state.app_ctx.repos.movie.clone(),
|
||||||
|
event_publisher: state.app_ctx.services.event_publisher.clone(),
|
||||||
|
};
|
||||||
|
match delete_review::execute(&deps, cmd).await {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
let redirect_url = form
|
let redirect_url = form
|
||||||
.redirect_after
|
.redirect_after
|
||||||
@@ -281,7 +305,13 @@ pub async fn get_export_html(
|
|||||||
user_id: user_id.value(),
|
user_id: user_id.value(),
|
||||||
format,
|
format,
|
||||||
};
|
};
|
||||||
match export_diary_uc::execute(&state.app_ctx, query).await {
|
match export_diary_uc::execute(
|
||||||
|
&state.app_ctx.repos.diary,
|
||||||
|
&state.app_ctx.services.diary_exporter,
|
||||||
|
query,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(bytes) => (
|
Ok(bytes) => (
|
||||||
StatusCode::OK,
|
StatusCode::OK,
|
||||||
[
|
[
|
||||||
@@ -332,7 +362,13 @@ pub async fn get_activity_feed_html(
|
|||||||
filter_following,
|
filter_following,
|
||||||
};
|
};
|
||||||
|
|
||||||
match application::diary::get_activity_feed::execute(&state.app_ctx, query).await {
|
let deps = GetActivityFeedDeps {
|
||||||
|
diary: state.app_ctx.repos.diary.clone(),
|
||||||
|
social_query: state.app_ctx.repos.social_query.clone(),
|
||||||
|
config: state.app_ctx.config.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
match application::diary::get_activity_feed::execute(&deps, query).await {
|
||||||
Ok(entries) => {
|
Ok(entries) => {
|
||||||
let entry_limit = entries.limit;
|
let entry_limit = entries.limit;
|
||||||
let entry_offset = entries.offset;
|
let entry_offset = entries.offset;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use uuid::Uuid;
|
|||||||
use application::{
|
use application::{
|
||||||
diary::{
|
diary::{
|
||||||
commands::SyncPosterCommand,
|
commands::SyncPosterCommand,
|
||||||
|
deps::{GetMovieSocialPageDeps},
|
||||||
get_movie_social_page, get_review_history,
|
get_movie_social_page, get_review_history,
|
||||||
queries::{GetMovieSocialPageQuery, GetReviewHistoryQuery},
|
queries::{GetMovieSocialPageQuery, GetReviewHistoryQuery},
|
||||||
},
|
},
|
||||||
@@ -88,7 +89,7 @@ pub async fn get_review_history(
|
|||||||
Path(movie_id): Path<Uuid>,
|
Path(movie_id): Path<Uuid>,
|
||||||
) -> Result<Json<ReviewHistoryResponse>, ApiError> {
|
) -> Result<Json<ReviewHistoryResponse>, ApiError> {
|
||||||
let (history, trend) =
|
let (history, trend) =
|
||||||
get_review_history::execute(&state.app_ctx, GetReviewHistoryQuery { movie_id }).await?;
|
get_review_history::execute(&state.app_ctx.repos.diary, GetReviewHistoryQuery { movie_id }).await?;
|
||||||
|
|
||||||
Ok(Json(ReviewHistoryResponse {
|
Ok(Json(ReviewHistoryResponse {
|
||||||
movie: crate::mappers::movies::movie_to_dto(history.movie()),
|
movie: crate::mappers::movies::movie_to_dto(history.movie()),
|
||||||
@@ -154,7 +155,11 @@ pub async fn get_movie_detail(
|
|||||||
let offset = params.offset.unwrap_or(0);
|
let offset = params.offset.unwrap_or(0);
|
||||||
|
|
||||||
let result = get_movie_social_page::execute(
|
let result = get_movie_social_page::execute(
|
||||||
&state.app_ctx,
|
&GetMovieSocialPageDeps {
|
||||||
|
movie: state.app_ctx.repos.movie.clone(),
|
||||||
|
diary: state.app_ctx.repos.diary.clone(),
|
||||||
|
movie_profile: state.app_ctx.repos.movie_profile.clone(),
|
||||||
|
},
|
||||||
GetMovieSocialPageQuery {
|
GetMovieSocialPageQuery {
|
||||||
movie_id,
|
movie_id,
|
||||||
limit,
|
limit,
|
||||||
@@ -288,7 +293,11 @@ pub async fn get_movie_detail_html(
|
|||||||
let offset = params.offset.unwrap_or(0);
|
let offset = params.offset.unwrap_or(0);
|
||||||
|
|
||||||
match get_movie_social_page::execute(
|
match get_movie_social_page::execute(
|
||||||
&state.app_ctx,
|
&GetMovieSocialPageDeps {
|
||||||
|
movie: state.app_ctx.repos.movie.clone(),
|
||||||
|
diary: state.app_ctx.repos.diary.clone(),
|
||||||
|
movie_profile: state.app_ctx.repos.movie_profile.clone(),
|
||||||
|
},
|
||||||
GetMovieSocialPageQuery {
|
GetMovieSocialPageQuery {
|
||||||
movie_id,
|
movie_id,
|
||||||
limit,
|
limit,
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ pub async fn get_feed(State(state): State<AppState>) -> Result<impl IntoResponse
|
|||||||
movie_id: None,
|
movie_id: None,
|
||||||
user_id: None,
|
user_id: None,
|
||||||
};
|
};
|
||||||
let page = get_diary::execute(&state.app_ctx, query).await?;
|
let page = get_diary::execute(&state.app_ctx.repos.diary, query).await?;
|
||||||
let xml = state
|
let xml = state
|
||||||
.rss_renderer
|
.rss_renderer
|
||||||
.render_feed(&page.items, "Movie Diary")
|
.render_feed(&page.items, "Movie Diary")
|
||||||
@@ -49,7 +49,7 @@ pub async fn get_user_feed(
|
|||||||
movie_id: None,
|
movie_id: None,
|
||||||
user_id: Some(user_id),
|
user_id: Some(user_id),
|
||||||
};
|
};
|
||||||
let page = get_diary::execute(&state.app_ctx, query).await?;
|
let page = get_diary::execute(&state.app_ctx.repos.diary, query).await?;
|
||||||
|
|
||||||
let display_name = user.email().value().split('@').next().unwrap_or("User");
|
let display_name = user.email().value().split('@').next().unwrap_or("User");
|
||||||
let title = format!("{}'s Movie Diary", display_name);
|
let title = format!("{}'s Movie Diary", display_name);
|
||||||
|
|||||||
Reference in New Issue
Block a user