feat: implement movie listing functionality with pagination and search
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use chrono::NaiveDateTime;
|
||||
use domain::models::{ExportFormat, FieldMapping, FileFormat, UserRole};
|
||||
use domain::models::{FieldMapping, FileFormat, UserRole};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct LogReviewCommand {
|
||||
@@ -21,11 +21,6 @@ pub struct SyncPosterCommand {
|
||||
pub external_metadata_id: String,
|
||||
}
|
||||
|
||||
pub struct LoginCommand {
|
||||
pub email: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
pub struct RegisterCommand {
|
||||
pub email: String,
|
||||
pub username: String,
|
||||
@@ -38,11 +33,6 @@ pub struct DeleteReviewCommand {
|
||||
pub requesting_user_id: Uuid,
|
||||
}
|
||||
|
||||
pub struct ExportCommand {
|
||||
pub user_id: Uuid,
|
||||
pub format: ExportFormat,
|
||||
}
|
||||
|
||||
// FileFormat is now in domain::models — no longer defined here
|
||||
|
||||
pub struct CreateImportSessionCommand {
|
||||
@@ -79,3 +69,10 @@ pub struct DeleteImportProfileCommand {
|
||||
pub user_id: Uuid,
|
||||
pub profile_id: Uuid,
|
||||
}
|
||||
|
||||
pub struct UpdateProfileCommand {
|
||||
pub user_id: Uuid,
|
||||
pub bio: Option<String>,
|
||||
pub avatar_bytes: Option<Vec<u8>>,
|
||||
pub avatar_content_type: Option<String>,
|
||||
}
|
||||
|
||||
@@ -226,9 +226,8 @@ mod tests {
|
||||
async fn upsert_movie(&self, _: &Movie) -> Result<(), DomainError> {
|
||||
panic!("unexpected")
|
||||
}
|
||||
async fn delete_movie(&self, _: &MovieId) -> Result<(), DomainError> {
|
||||
panic!("unexpected")
|
||||
}
|
||||
async fn delete_movie(&self, _: &MovieId) -> Result<(), DomainError> { panic!("unexpected") }
|
||||
async fn list_movies(&self, _: &domain::models::collections::PageParams, _: Option<&str>) -> Result<domain::models::collections::Paginated<Movie>, DomainError> { panic!("unexpected") }
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -249,12 +248,9 @@ mod tests {
|
||||
) -> Result<Vec<Movie>, DomainError> {
|
||||
Ok(vec![])
|
||||
}
|
||||
async fn upsert_movie(&self, _: &Movie) -> Result<(), DomainError> {
|
||||
panic!("unexpected")
|
||||
}
|
||||
async fn delete_movie(&self, _: &MovieId) -> Result<(), DomainError> {
|
||||
panic!("unexpected")
|
||||
}
|
||||
async fn upsert_movie(&self, _: &Movie) -> Result<(), DomainError> { panic!("unexpected") }
|
||||
async fn delete_movie(&self, _: &MovieId) -> Result<(), DomainError> { panic!("unexpected") }
|
||||
async fn list_movies(&self, _: &domain::models::collections::PageParams, _: Option<&str>) -> Result<domain::models::collections::Paginated<Movie>, DomainError> { panic!("unexpected") }
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -275,12 +271,9 @@ mod tests {
|
||||
) -> Result<Vec<Movie>, DomainError> {
|
||||
Ok(vec![self.0.clone()])
|
||||
}
|
||||
async fn upsert_movie(&self, _: &Movie) -> Result<(), DomainError> {
|
||||
panic!("unexpected")
|
||||
}
|
||||
async fn delete_movie(&self, _: &MovieId) -> Result<(), DomainError> {
|
||||
panic!("unexpected")
|
||||
}
|
||||
async fn upsert_movie(&self, _: &Movie) -> Result<(), DomainError> { panic!("unexpected") }
|
||||
async fn delete_movie(&self, _: &MovieId) -> Result<(), DomainError> { panic!("unexpected") }
|
||||
async fn list_movies(&self, _: &domain::models::collections::PageParams, _: Option<&str>) -> Result<domain::models::collections::Paginated<Movie>, DomainError> { panic!("unexpected") }
|
||||
}
|
||||
|
||||
struct MetaReturnsMovie(Movie);
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
use domain::models::SortDirection;
|
||||
use domain::models::{ExportFormat, SortDirection};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct LoginQuery {
|
||||
pub email: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
pub struct ExportQuery {
|
||||
pub user_id: Uuid,
|
||||
pub format: ExportFormat,
|
||||
}
|
||||
|
||||
pub struct GetDiaryQuery {
|
||||
pub limit: Option<u32>,
|
||||
pub offset: Option<u32>,
|
||||
@@ -70,3 +80,9 @@ pub struct GetMovieSocialPageQuery {
|
||||
pub limit: u32,
|
||||
pub offset: u32,
|
||||
}
|
||||
|
||||
pub struct GetMoviesQuery {
|
||||
pub limit: Option<u32>,
|
||||
pub offset: Option<u32>,
|
||||
pub search: Option<String>,
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use domain::{errors::DomainError, value_objects::UserId};
|
||||
|
||||
use crate::{commands::ExportCommand, context::AppContext};
|
||||
use crate::{context::AppContext, queries::ExportQuery};
|
||||
|
||||
pub async fn execute(ctx: &AppContext, cmd: ExportCommand) -> Result<Vec<u8>, DomainError> {
|
||||
pub async fn execute(ctx: &AppContext, query: ExportQuery) -> Result<Vec<u8>, DomainError> {
|
||||
let entries = ctx
|
||||
.diary_repository
|
||||
.get_user_history(&UserId::from_uuid(cmd.user_id))
|
||||
.get_user_history(&UserId::from_uuid(query.user_id))
|
||||
.await?;
|
||||
ctx.diary_exporter
|
||||
.serialize_entries(&entries, cmd.format)
|
||||
.serialize_entries(&entries, query.format)
|
||||
.await
|
||||
}
|
||||
|
||||
14
crates/application/src/use_cases/get_movies.rs
Normal file
14
crates/application/src/use_cases/get_movies.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use domain::{
|
||||
errors::DomainError,
|
||||
models::collections::{PageParams, Paginated},
|
||||
models::Movie,
|
||||
};
|
||||
|
||||
use crate::{context::AppContext, queries::GetMoviesQuery};
|
||||
|
||||
pub async fn execute(ctx: &AppContext, query: GetMoviesQuery) -> Result<Paginated<Movie>, DomainError> {
|
||||
let page = PageParams::new(query.limit, query.offset)?;
|
||||
ctx.movie_repository
|
||||
.list_movies(&page, query.search.as_deref())
|
||||
.await
|
||||
}
|
||||
@@ -3,7 +3,7 @@ use uuid::Uuid;
|
||||
|
||||
use domain::{errors::DomainError, value_objects::Email};
|
||||
|
||||
use crate::{commands::LoginCommand, context::AppContext};
|
||||
use crate::{context::AppContext, queries::LoginQuery};
|
||||
|
||||
pub struct LoginResult {
|
||||
pub token: String,
|
||||
@@ -12,8 +12,8 @@ pub struct LoginResult {
|
||||
pub expires_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
pub async fn execute(ctx: &AppContext, cmd: LoginCommand) -> Result<LoginResult, DomainError> {
|
||||
let email = Email::new(cmd.email)?;
|
||||
pub async fn execute(ctx: &AppContext, query: LoginQuery) -> Result<LoginResult, DomainError> {
|
||||
let email = Email::new(query.email)?;
|
||||
let user = ctx
|
||||
.user_repository
|
||||
.find_by_email(&email)
|
||||
@@ -22,7 +22,7 @@ pub async fn execute(ctx: &AppContext, cmd: LoginCommand) -> Result<LoginResult,
|
||||
|
||||
let valid = ctx
|
||||
.password_hasher
|
||||
.verify(&cmd.password, user.password_hash())
|
||||
.verify(&query.password, user.password_hash())
|
||||
.await?;
|
||||
if !valid {
|
||||
return Err(DomainError::Unauthorized("Invalid credentials".into()));
|
||||
|
||||
@@ -11,6 +11,7 @@ pub mod export_diary;
|
||||
pub mod get_activity_feed;
|
||||
pub mod get_diary;
|
||||
pub mod get_movie_social_page;
|
||||
pub mod get_movies;
|
||||
pub mod get_review_history;
|
||||
pub mod get_user_profile;
|
||||
pub mod get_users;
|
||||
|
||||
@@ -4,14 +4,7 @@ use domain::{
|
||||
value_objects::UserId,
|
||||
};
|
||||
|
||||
use crate::context::AppContext;
|
||||
|
||||
pub struct UpdateProfileCommand {
|
||||
pub user_id: uuid::Uuid,
|
||||
pub bio: Option<String>,
|
||||
pub avatar_bytes: Option<Vec<u8>>,
|
||||
pub avatar_content_type: Option<String>,
|
||||
}
|
||||
use crate::{commands::UpdateProfileCommand, context::AppContext};
|
||||
|
||||
pub async fn execute(ctx: &AppContext, cmd: UpdateProfileCommand) -> Result<(), DomainError> {
|
||||
let user_id = UserId::from_uuid(cmd.user_id);
|
||||
|
||||
Reference in New Issue
Block a user