export feature
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
use domain::{errors::DomainError, value_objects::{ReviewId, UserId}};
|
||||
use crate::{commands::DeleteReviewCommand, context::AppContext};
|
||||
use domain::{
|
||||
errors::DomainError,
|
||||
value_objects::{ReviewId, UserId},
|
||||
};
|
||||
|
||||
pub async fn execute(ctx: &AppContext, cmd: DeleteReviewCommand) -> Result<(), DomainError> {
|
||||
let review_id = ReviewId::from_uuid(cmd.review_id);
|
||||
|
||||
@@ -1,23 +1,13 @@
|
||||
use std::sync::Arc;
|
||||
use domain::{errors::DomainError, value_objects::UserId};
|
||||
|
||||
use domain::{
|
||||
errors::DomainError,
|
||||
ports::{DiaryExporter, DiaryRepository},
|
||||
};
|
||||
use crate::{commands::ExportCommand, context::AppContext};
|
||||
|
||||
use crate::commands::ExportCommand;
|
||||
|
||||
pub struct ExportDiary {
|
||||
repository: Arc<dyn DiaryRepository>,
|
||||
exporter: Arc<dyn DiaryExporter>,
|
||||
}
|
||||
|
||||
impl ExportDiary {
|
||||
pub async fn execute(&self, req: ExportCommand) -> Result<Vec<u8>, DomainError> {
|
||||
// 1. fetch all diary entries for the user
|
||||
// 2. delegate serialization to the port (exporter)
|
||||
|
||||
// Return bytes of the exported diary, which can be written to a file or returned in an HTTP response
|
||||
Ok(vec![])
|
||||
}
|
||||
pub async fn execute(ctx: &AppContext, cmd: ExportCommand) -> Result<Vec<u8>, DomainError> {
|
||||
let entries = ctx
|
||||
.diary_repository
|
||||
.get_user_history(&UserId::from_uuid(cmd.user_id))
|
||||
.await?;
|
||||
ctx.diary_exporter
|
||||
.serialize_entries(&entries, cmd.format)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
use crate::{context::AppContext, queries::GetActivityFeedQuery};
|
||||
use domain::{
|
||||
errors::DomainError,
|
||||
models::{FeedEntry, collections::{PageParams, Paginated}},
|
||||
models::{
|
||||
FeedEntry,
|
||||
collections::{PageParams, Paginated},
|
||||
},
|
||||
};
|
||||
use crate::{context::AppContext, queries::GetActivityFeedQuery};
|
||||
|
||||
pub async fn execute(
|
||||
ctx: &AppContext,
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
use crate::{
|
||||
context::AppContext,
|
||||
queries::{GetUserProfileQuery, ProfileView},
|
||||
};
|
||||
use chrono::Datelike;
|
||||
use domain::{
|
||||
errors::DomainError,
|
||||
@@ -7,7 +11,6 @@ use domain::{
|
||||
},
|
||||
value_objects::UserId,
|
||||
};
|
||||
use crate::{context::AppContext, queries::{GetUserProfileQuery, ProfileView}};
|
||||
|
||||
pub struct UserProfileData {
|
||||
pub stats: UserStats,
|
||||
@@ -27,26 +30,61 @@ pub async fn execute(
|
||||
ProfileView::History => {
|
||||
let all_entries = ctx.diary_repository.get_user_history(&user_id).await?;
|
||||
let history = group_by_month(all_entries);
|
||||
Ok(UserProfileData { stats, entries: None, history: Some(history), trends: None })
|
||||
Ok(UserProfileData {
|
||||
stats,
|
||||
entries: None,
|
||||
history: Some(history),
|
||||
trends: None,
|
||||
})
|
||||
}
|
||||
ProfileView::Trends => {
|
||||
let trends = ctx.stats_repository.get_user_trends(&user_id).await?;
|
||||
Ok(UserProfileData { stats, entries: None, history: None, trends: Some(trends) })
|
||||
Ok(UserProfileData {
|
||||
stats,
|
||||
entries: None,
|
||||
history: None,
|
||||
trends: Some(trends),
|
||||
})
|
||||
}
|
||||
ProfileView::Ratings => {
|
||||
let filter = paged_user_filter(user_id, SortDirection::ByRatingDesc, query.limit, query.offset)?;
|
||||
let filter = paged_user_filter(
|
||||
user_id,
|
||||
SortDirection::ByRatingDesc,
|
||||
query.limit,
|
||||
query.offset,
|
||||
)?;
|
||||
let entries = ctx.diary_repository.query_diary(&filter).await?;
|
||||
Ok(UserProfileData { stats, entries: Some(entries), history: None, trends: None })
|
||||
Ok(UserProfileData {
|
||||
stats,
|
||||
entries: Some(entries),
|
||||
history: None,
|
||||
trends: None,
|
||||
})
|
||||
}
|
||||
ProfileView::Recent => {
|
||||
let filter = paged_user_filter(user_id, SortDirection::Descending, query.limit, query.offset)?;
|
||||
let filter = paged_user_filter(
|
||||
user_id,
|
||||
SortDirection::Descending,
|
||||
query.limit,
|
||||
query.offset,
|
||||
)?;
|
||||
let entries = ctx.diary_repository.query_diary(&filter).await?;
|
||||
Ok(UserProfileData { stats, entries: Some(entries), history: None, trends: None })
|
||||
Ok(UserProfileData {
|
||||
stats,
|
||||
entries: Some(entries),
|
||||
history: None,
|
||||
trends: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn paged_user_filter(user_id: UserId, sort_by: SortDirection, limit: Option<u32>, offset: Option<u32>) -> Result<DiaryFilter, DomainError> {
|
||||
fn paged_user_filter(
|
||||
user_id: UserId,
|
||||
sort_by: SortDirection,
|
||||
limit: Option<u32>,
|
||||
offset: Option<u32>,
|
||||
) -> Result<DiaryFilter, DomainError> {
|
||||
let page = PageParams::new(limit, offset)?;
|
||||
Ok(DiaryFilter {
|
||||
sort_by,
|
||||
@@ -81,11 +119,22 @@ fn group_by_month(entries: Vec<DiaryEntry>) -> Vec<MonthActivity> {
|
||||
|
||||
fn format_year_month_long(ym: &str) -> String {
|
||||
let parts: Vec<&str> = ym.splitn(2, '-').collect();
|
||||
if parts.len() != 2 { return ym.to_string(); }
|
||||
if parts.len() != 2 {
|
||||
return ym.to_string();
|
||||
}
|
||||
let month = match parts[1] {
|
||||
"01" => "January", "02" => "February", "03" => "March", "04" => "April",
|
||||
"05" => "May", "06" => "June", "07" => "July", "08" => "August",
|
||||
"09" => "September", "10" => "October", "11" => "November", "12" => "December",
|
||||
"01" => "January",
|
||||
"02" => "February",
|
||||
"03" => "March",
|
||||
"04" => "April",
|
||||
"05" => "May",
|
||||
"06" => "June",
|
||||
"07" => "July",
|
||||
"08" => "August",
|
||||
"09" => "September",
|
||||
"10" => "October",
|
||||
"11" => "November",
|
||||
"12" => "December",
|
||||
_ => parts[1],
|
||||
};
|
||||
format!("{} {}", month, parts[0])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use domain::{errors::DomainError, models::UserSummary};
|
||||
use crate::{context::AppContext, queries::GetUsersQuery};
|
||||
use domain::{errors::DomainError, models::UserSummary};
|
||||
|
||||
pub async fn execute(
|
||||
ctx: &AppContext,
|
||||
|
||||
@@ -20,7 +20,9 @@ pub async fn execute(ctx: &AppContext, cmd: LogReviewCommand) -> Result<(), Doma
|
||||
repository: ctx.movie_repository.as_ref(),
|
||||
metadata_client: ctx.metadata_client.as_ref(),
|
||||
};
|
||||
let (movie, is_new_movie) = MovieResolver::default_pipeline().resolve(&cmd, &deps).await?;
|
||||
let (movie, is_new_movie) = MovieResolver::default_pipeline()
|
||||
.resolve(&cmd, &deps)
|
||||
.await?;
|
||||
|
||||
ctx.movie_repository.upsert_movie(&movie).await?;
|
||||
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
use domain::{errors::DomainError, models::User, value_objects::{Email, Username}};
|
||||
use domain::{
|
||||
errors::DomainError,
|
||||
models::User,
|
||||
value_objects::{Email, Username},
|
||||
};
|
||||
|
||||
use crate::{commands::RegisterCommand, context::AppContext};
|
||||
|
||||
@@ -19,13 +23,24 @@ pub async fn execute(ctx: &AppContext, cmd: RegisterCommand) -> Result<(), Domai
|
||||
let username = Username::new(cmd.username)?;
|
||||
|
||||
if ctx.user_repository.find_by_email(&email).await?.is_some() {
|
||||
return Err(DomainError::ValidationError("Email already registered".into()));
|
||||
return Err(DomainError::ValidationError(
|
||||
"Email already registered".into(),
|
||||
));
|
||||
}
|
||||
|
||||
if ctx.user_repository.find_by_username(&username).await?.is_some() {
|
||||
return Err(DomainError::ValidationError("Username already taken".into()));
|
||||
if ctx
|
||||
.user_repository
|
||||
.find_by_username(&username)
|
||||
.await?
|
||||
.is_some()
|
||||
{
|
||||
return Err(DomainError::ValidationError(
|
||||
"Username already taken".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let hash = ctx.password_hasher.hash(&cmd.password).await?;
|
||||
ctx.user_repository.save(&User::new(email, username, hash)).await
|
||||
ctx.user_repository
|
||||
.save(&User::new(email, username, hash))
|
||||
.await
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user