structural refactor and codebase improvements

This commit is contained in:
2026-08-09 14:58:14 +02:00
parent 22b1dd3f56
commit c9715baab8
247 changed files with 11515 additions and 3063 deletions

View File

@@ -1,22 +1,16 @@
use std::sync::Arc;
use domain::{
errors::DomainError,
models::WatchEventStatus,
ports::{WatchEventCommand, WatchEventQuery},
value_objects::{UserId, WatchEventId},
};
use crate::{
diary::commands::{LogReviewCommand, MovieInput},
integrations::commands::ConfirmWatchEventsCommand,
ports::ReviewLogger,
integrations::{commands::ConfirmWatchEventsCommand, deps::ConfirmWatchEventsDeps},
};
pub async fn execute(
watch_event_command: Arc<dyn WatchEventCommand>,
watch_event_query: Arc<dyn WatchEventQuery>,
review_logger: Arc<dyn ReviewLogger>,
deps: &ConfirmWatchEventsDeps,
cmd: ConfirmWatchEventsCommand,
) -> Result<u32, DomainError> {
let user_id = UserId::from_uuid(cmd.user_id);
@@ -24,7 +18,8 @@ pub async fn execute(
for c in cmd.confirmations {
let event_id = WatchEventId::from_uuid(c.watch_event_id);
let event = watch_event_query
let event = deps
.watch_event_query
.get_by_id(&event_id)
.await?
.ok_or_else(|| DomainError::NotFound(format!("WatchEvent {}", c.watch_event_id)))?;
@@ -60,9 +55,9 @@ pub async fn execute(
watch_medium: Some(domain::value_objects::WatchMedium::MediaServer),
};
review_logger.log_review(review_cmd).await?;
deps.review_logger.log_review(review_cmd).await?;
watch_event_command
deps.watch_event_command
.update_status(&event_id, WatchEventStatus::Confirmed)
.await?;

View File

@@ -2,9 +2,38 @@ use std::sync::Arc;
use domain::ports::{EventPublisher, WatchEventCommand, WatchEventQuery, WebhookTokenRepository};
use crate::ports::ReviewLogger;
pub struct IngestWatchEventDeps {
pub webhook_token: Arc<dyn WebhookTokenRepository>,
pub watch_event_command: Arc<dyn WatchEventCommand>,
pub watch_event_query: Arc<dyn WatchEventQuery>,
pub event_publisher: Arc<dyn EventPublisher>,
}
pub struct ConfirmWatchEventsDeps {
pub watch_event_command: Arc<dyn WatchEventCommand>,
pub watch_event_query: Arc<dyn WatchEventQuery>,
pub review_logger: Arc<dyn ReviewLogger>,
}
pub struct DismissWatchEventsDeps {
pub watch_event_command: Arc<dyn WatchEventCommand>,
pub watch_event_query: Arc<dyn WatchEventQuery>,
}
pub struct GenerateWebhookTokenDeps {
pub webhook_token: Arc<dyn WebhookTokenRepository>,
}
pub struct GetWatchQueueDeps {
pub watch_event_query: Arc<dyn WatchEventQuery>,
}
pub struct GetWebhookTokensDeps {
pub webhook_token: Arc<dyn WebhookTokenRepository>,
}
pub struct RevokeWebhookTokenDeps {
pub webhook_token: Arc<dyn WebhookTokenRepository>,
}

View File

@@ -1,17 +1,13 @@
use std::sync::Arc;
use domain::{
errors::DomainError,
models::WatchEventStatus,
ports::{WatchEventCommand, WatchEventQuery},
value_objects::{UserId, WatchEventId},
};
use crate::integrations::commands::DismissWatchEventsCommand;
use crate::integrations::{commands::DismissWatchEventsCommand, deps::DismissWatchEventsDeps};
pub async fn execute(
watch_event_command: Arc<dyn WatchEventCommand>,
watch_event_query: Arc<dyn WatchEventQuery>,
deps: &DismissWatchEventsDeps,
cmd: DismissWatchEventsCommand,
) -> Result<u32, DomainError> {
let user_id = UserId::from_uuid(cmd.user_id);
@@ -25,7 +21,7 @@ pub async fn execute(
.map(|id| WatchEventId::from_uuid(*id))
.collect();
let events = watch_event_query.get_by_ids(&ids).await?;
let events = deps.watch_event_query.get_by_ids(&ids).await?;
if events.len() != ids.len() {
return Err(DomainError::NotFound(
@@ -38,7 +34,8 @@ pub async fn execute(
}
}
let count = watch_event_command
let count = deps
.watch_event_command
.update_status_batch(&ids, WatchEventStatus::Dismissed)
.await?;

View File

@@ -1,11 +1,7 @@
use std::sync::Arc;
use domain::{
errors::DomainError, models::WebhookToken, ports::WebhookTokenRepository, value_objects::UserId,
};
use domain::{errors::DomainError, models::WebhookToken, value_objects::UserId};
use sha2::{Digest, Sha256};
use crate::integrations::commands::GenerateWebhookTokenCommand;
use crate::integrations::{commands::GenerateWebhookTokenCommand, deps::GenerateWebhookTokenDeps};
pub struct GeneratedWebhookToken {
pub token_plaintext: String,
@@ -13,7 +9,7 @@ pub struct GeneratedWebhookToken {
}
pub async fn execute(
webhook_token: Arc<dyn WebhookTokenRepository>,
deps: &GenerateWebhookTokenDeps,
cmd: GenerateWebhookTokenCommand,
) -> Result<GeneratedWebhookToken, DomainError> {
let plaintext = generate_random_token();
@@ -22,7 +18,7 @@ pub async fn execute(
let user_id = UserId::from_uuid(cmd.user_id);
let token = WebhookToken::new(user_id, hash, cmd.provider, cmd.label);
webhook_token.save(&token).await?;
deps.webhook_token.save(&token).await?;
Ok(GeneratedWebhookToken {
token_plaintext: plaintext,

View File

@@ -1,17 +1,13 @@
use std::sync::Arc;
use domain::{errors::DomainError, models::WatchEvent, value_objects::UserId};
use domain::{
errors::DomainError, models::WatchEvent, ports::WatchEventQuery, value_objects::UserId,
};
use crate::integrations::queries::GetWatchQueueQuery;
use crate::integrations::{deps::GetWatchQueueDeps, queries::GetWatchQueueQuery};
pub async fn execute(
watch_event_query: Arc<dyn WatchEventQuery>,
deps: &GetWatchQueueDeps,
query: GetWatchQueueQuery,
) -> Result<Vec<WatchEvent>, DomainError> {
let user_id = UserId::from_uuid(query.user_id);
watch_event_query.list_pending(&user_id).await
deps.watch_event_query.list_pending(&user_id).await
}
#[cfg(test)]

View File

@@ -1,17 +1,13 @@
use std::sync::Arc;
use domain::{errors::DomainError, models::WebhookToken, value_objects::UserId};
use domain::{
errors::DomainError, models::WebhookToken, ports::WebhookTokenRepository, value_objects::UserId,
};
use crate::integrations::queries::GetWebhookTokensQuery;
use crate::integrations::{deps::GetWebhookTokensDeps, queries::GetWebhookTokensQuery};
pub async fn execute(
webhook_token: Arc<dyn WebhookTokenRepository>,
deps: &GetWebhookTokensDeps,
query: GetWebhookTokensQuery,
) -> Result<Vec<WebhookToken>, DomainError> {
let user_id = UserId::from_uuid(query.user_id);
webhook_token.list_by_user(&user_id).await
deps.webhook_token.list_by_user(&user_id).await
}
#[cfg(test)]

View File

@@ -1,20 +1,17 @@
use std::sync::Arc;
use domain::{
errors::DomainError,
ports::WebhookTokenRepository,
value_objects::{UserId, WebhookTokenId},
};
use crate::integrations::commands::RevokeWebhookTokenCommand;
use crate::integrations::{commands::RevokeWebhookTokenCommand, deps::RevokeWebhookTokenDeps};
pub async fn execute(
webhook_token: Arc<dyn WebhookTokenRepository>,
deps: &RevokeWebhookTokenDeps,
cmd: RevokeWebhookTokenCommand,
) -> Result<(), DomainError> {
let user_id = UserId::from_uuid(cmd.user_id);
let token_id = WebhookTokenId::from_uuid(cmd.token_id);
webhook_token.delete(&token_id, &user_id).await
deps.webhook_token.delete(&token_id, &user_id).await
}
#[cfg(test)]

View File

@@ -8,12 +8,24 @@ use uuid::Uuid;
use crate::integrations::commands::{ConfirmWatchEventsCommand, WatchEventConfirmation};
use crate::integrations::confirm;
use crate::integrations::deps::ConfirmWatchEventsDeps;
use crate::test_helpers::NoopReviewLogger;
fn noop_logger() -> Arc<dyn crate::ports::ReviewLogger> {
Arc::new(NoopReviewLogger)
}
fn deps(
watch_events: &Arc<InMemoryWatchEventRepository>,
review_logger: Arc<dyn crate::ports::ReviewLogger>,
) -> ConfirmWatchEventsDeps {
ConfirmWatchEventsDeps {
watch_event_command: Arc::clone(watch_events) as _,
watch_event_query: Arc::clone(watch_events) as _,
review_logger,
}
}
#[tokio::test]
async fn confirms_watch_event_via_review_logger() {
let watch_events = InMemoryWatchEventRepository::new();
@@ -32,9 +44,7 @@ async fn confirms_watch_event_via_review_logger() {
watch_events.save(&event).await.unwrap();
let result = confirm::execute(
Arc::clone(&watch_events) as _,
Arc::clone(&watch_events) as _,
noop_logger(),
&deps(&watch_events, noop_logger()),
ConfirmWatchEventsCommand {
user_id: uid,
confirmations: vec![WatchEventConfirmation {
@@ -55,9 +65,7 @@ async fn empty_confirmations_returns_zero() {
let watch_events = InMemoryWatchEventRepository::new();
let result = confirm::execute(
Arc::clone(&watch_events) as _,
Arc::clone(&watch_events) as _,
noop_logger(),
&deps(&watch_events, noop_logger()),
ConfirmWatchEventsCommand {
user_id: Uuid::new_v4(),
confirmations: vec![],
@@ -87,9 +95,7 @@ async fn confirms_event_with_external_metadata_id_and_no_movie_id() {
watch_events.save(&event).await.unwrap();
let result = confirm::execute(
Arc::clone(&watch_events) as _,
Arc::clone(&watch_events) as _,
noop_logger(),
&deps(&watch_events, noop_logger()),
ConfirmWatchEventsCommand {
user_id: uid,
confirmations: vec![WatchEventConfirmation {
@@ -124,9 +130,7 @@ async fn rejects_other_users_event() {
watch_events.save(&event).await.unwrap();
let result = confirm::execute(
Arc::clone(&watch_events) as _,
Arc::clone(&watch_events) as _,
noop_logger(),
&deps(&watch_events, noop_logger()),
ConfirmWatchEventsCommand {
user_id: intruder,
confirmations: vec![WatchEventConfirmation {
@@ -146,9 +150,7 @@ async fn fails_when_event_not_found() {
let watch_events = InMemoryWatchEventRepository::new();
let result = confirm::execute(
Arc::clone(&watch_events) as _,
Arc::clone(&watch_events) as _,
noop_logger(),
&deps(&watch_events, noop_logger()),
ConfirmWatchEventsCommand {
user_id: Uuid::new_v4(),
confirmations: vec![WatchEventConfirmation {
@@ -208,9 +210,7 @@ async fn confirms_event_with_movie_id() {
));
let result = confirm::execute(
Arc::clone(&watch_events) as _,
Arc::clone(&watch_events) as _,
review_logger,
&deps(&watch_events, review_logger),
ConfirmWatchEventsCommand {
user_id: uid,
confirmations: vec![WatchEventConfirmation {
@@ -244,9 +244,7 @@ async fn confirms_event_without_movie_id_and_without_external_metadata_id() {
watch_events.save(&event).await.unwrap();
let result = confirm::execute(
Arc::clone(&watch_events) as _,
Arc::clone(&watch_events) as _,
noop_logger(),
&deps(&watch_events, noop_logger()),
ConfirmWatchEventsCommand {
user_id: uid,
confirmations: vec![WatchEventConfirmation {
@@ -293,9 +291,7 @@ async fn confirms_multiple_events() {
watch_events.save(&event2).await.unwrap();
let result = confirm::execute(
Arc::clone(&watch_events) as _,
Arc::clone(&watch_events) as _,
noop_logger(),
&deps(&watch_events, noop_logger()),
ConfirmWatchEventsCommand {
user_id: uid,
confirmations: vec![
@@ -336,9 +332,7 @@ async fn confirms_event_without_year() {
watch_events.save(&event).await.unwrap();
let result = confirm::execute(
Arc::clone(&watch_events) as _,
Arc::clone(&watch_events) as _,
noop_logger(),
&deps(&watch_events, noop_logger()),
ConfirmWatchEventsCommand {
user_id: uid,
confirmations: vec![WatchEventConfirmation {

View File

@@ -6,15 +6,22 @@ use domain::testing::InMemoryWatchEventRepository;
use domain::value_objects::UserId;
use uuid::Uuid;
use crate::integrations::deps::DismissWatchEventsDeps;
use crate::integrations::{commands::DismissWatchEventsCommand, dismiss};
fn deps(watch_events: &Arc<InMemoryWatchEventRepository>) -> DismissWatchEventsDeps {
DismissWatchEventsDeps {
watch_event_command: Arc::clone(watch_events) as _,
watch_event_query: Arc::clone(watch_events) as _,
}
}
#[tokio::test]
async fn dismisses_empty_list_returns_zero() {
let events = InMemoryWatchEventRepository::new();
let result = dismiss::execute(
Arc::clone(&events) as _,
Arc::clone(&events) as _,
&deps(&events),
DismissWatchEventsCommand {
user_id: Uuid::new_v4(),
event_ids: vec![],
@@ -31,8 +38,7 @@ async fn fails_when_event_not_found() {
let events = InMemoryWatchEventRepository::new();
let result = dismiss::execute(
Arc::clone(&events) as _,
Arc::clone(&events) as _,
&deps(&events),
DismissWatchEventsCommand {
user_id: Uuid::new_v4(),
event_ids: vec![Uuid::new_v4()],
@@ -73,8 +79,7 @@ async fn dismisses_existing_events() {
watch_events.save(&e2).await.unwrap();
let result = dismiss::execute(
Arc::clone(&watch_events) as _,
Arc::clone(&watch_events) as _,
&deps(&watch_events),
DismissWatchEventsCommand {
user_id: uid,
event_ids: vec![id1, id2],

View File

@@ -5,6 +5,7 @@ use domain::ports::WebhookTokenRepository;
use domain::testing::InMemoryWebhookTokenRepository;
use uuid::Uuid;
use crate::integrations::deps::GenerateWebhookTokenDeps;
use crate::integrations::{commands::GenerateWebhookTokenCommand, generate_token};
#[tokio::test]
@@ -13,7 +14,9 @@ async fn generates_token_and_saves() {
let user_id = Uuid::new_v4();
let result = generate_token::execute(
Arc::clone(&tokens),
&GenerateWebhookTokenDeps {
webhook_token: Arc::clone(&tokens),
},
GenerateWebhookTokenCommand {
user_id,
provider: WatchEventSource::Jellyfin,

View File

@@ -7,14 +7,21 @@ use domain::testing::InMemoryWatchEventRepository;
use domain::value_objects::UserId;
use uuid::Uuid;
use crate::integrations::deps::GetWatchQueueDeps;
use crate::integrations::{get_queue, queries::GetWatchQueueQuery};
fn deps(events: &Arc<InMemoryWatchEventRepository>) -> GetWatchQueueDeps {
GetWatchQueueDeps {
watch_event_query: Arc::clone(events) as _,
}
}
#[tokio::test]
async fn returns_empty_when_no_events() {
let events = InMemoryWatchEventRepository::new();
let result = get_queue::execute(
Arc::clone(&events) as _,
&deps(&events),
GetWatchQueueQuery {
user_id: Uuid::new_v4(),
},
@@ -41,7 +48,7 @@ async fn returns_pending_events() {
);
events.save(&event).await.unwrap();
let result = get_queue::execute(Arc::clone(&events) as _, GetWatchQueueQuery { user_id })
let result = get_queue::execute(&deps(&events), GetWatchQueueQuery { user_id })
.await
.unwrap();

View File

@@ -5,17 +5,30 @@ use domain::ports::WebhookTokenRepository;
use domain::testing::InMemoryWebhookTokenRepository;
use uuid::Uuid;
use crate::integrations::deps::{GenerateWebhookTokenDeps, GetWebhookTokensDeps};
use crate::integrations::{
commands::GenerateWebhookTokenCommand, generate_token, get_tokens,
queries::GetWebhookTokensQuery,
};
fn generate_deps(tokens: &Arc<dyn WebhookTokenRepository>) -> GenerateWebhookTokenDeps {
GenerateWebhookTokenDeps {
webhook_token: Arc::clone(tokens),
}
}
fn get_deps(tokens: &Arc<dyn WebhookTokenRepository>) -> GetWebhookTokensDeps {
GetWebhookTokensDeps {
webhook_token: Arc::clone(tokens),
}
}
#[tokio::test]
async fn returns_empty_when_no_tokens() {
let tokens: Arc<dyn WebhookTokenRepository> = InMemoryWebhookTokenRepository::new();
let result = get_tokens::execute(
Arc::clone(&tokens),
&get_deps(&tokens),
GetWebhookTokensQuery {
user_id: Uuid::new_v4(),
},
@@ -33,7 +46,7 @@ async fn returns_tokens_after_generate() {
let user_id = Uuid::new_v4();
generate_token::execute(
Arc::clone(&tokens),
&generate_deps(&tokens),
GenerateWebhookTokenCommand {
user_id,
provider: WatchEventSource::Jellyfin,
@@ -44,7 +57,7 @@ async fn returns_tokens_after_generate() {
.unwrap();
generate_token::execute(
Arc::clone(&tokens),
&generate_deps(&tokens),
GenerateWebhookTokenCommand {
user_id,
provider: WatchEventSource::Plex,
@@ -54,7 +67,7 @@ async fn returns_tokens_after_generate() {
.await
.unwrap();
let result = get_tokens::execute(Arc::clone(&tokens), GetWebhookTokensQuery { user_id })
let result = get_tokens::execute(&get_deps(&tokens), GetWebhookTokensQuery { user_id })
.await
.unwrap();

View File

@@ -8,7 +8,7 @@ use domain::testing::{
use uuid::Uuid;
use crate::integrations::commands::{GenerateWebhookTokenCommand, IngestWatchEventCommand};
use crate::integrations::deps::IngestWatchEventDeps;
use crate::integrations::deps::{GenerateWebhookTokenDeps, IngestWatchEventDeps};
use crate::integrations::{generate_token, ingest};
struct FakeParser;
@@ -35,7 +35,9 @@ async fn ingests_watch_event() {
let user_id = Uuid::new_v4();
let generated = generate_token::execute(
Arc::clone(&tokens),
&GenerateWebhookTokenDeps {
webhook_token: Arc::clone(&tokens),
},
GenerateWebhookTokenCommand {
user_id,
provider: WatchEventSource::Jellyfin,

View File

@@ -5,6 +5,9 @@ use domain::ports::WebhookTokenRepository;
use domain::testing::InMemoryWebhookTokenRepository;
use uuid::Uuid;
use crate::integrations::deps::{
GenerateWebhookTokenDeps, GetWebhookTokensDeps, RevokeWebhookTokenDeps,
};
use crate::integrations::{
commands::{GenerateWebhookTokenCommand, RevokeWebhookTokenCommand},
generate_token, get_tokens,
@@ -19,7 +22,9 @@ async fn revokes_existing_token() {
let user_id = Uuid::new_v4();
let generated = generate_token::execute(
Arc::clone(&tokens),
&GenerateWebhookTokenDeps {
webhook_token: Arc::clone(&tokens),
},
GenerateWebhookTokenCommand {
user_id,
provider: WatchEventSource::Jellyfin,
@@ -32,15 +37,22 @@ async fn revokes_existing_token() {
let token_id = generated.token.id().value();
revoke_token::execute(
Arc::clone(&tokens),
&RevokeWebhookTokenDeps {
webhook_token: Arc::clone(&tokens),
},
RevokeWebhookTokenCommand { user_id, token_id },
)
.await
.unwrap();
let remaining = get_tokens::execute(Arc::clone(&tokens), GetWebhookTokensQuery { user_id })
.await
.unwrap();
let remaining = get_tokens::execute(
&GetWebhookTokensDeps {
webhook_token: Arc::clone(&tokens),
},
GetWebhookTokensQuery { user_id },
)
.await
.unwrap();
assert!(remaining.is_empty());
}