refactor(person): EnrichPersonDeps + GetPersonDeps, PersonEnrichmentHandler
This commit is contained in:
@@ -1,15 +1,31 @@
|
|||||||
use async_trait::async_trait;
|
use std::sync::Arc;
|
||||||
use domain::{errors::DomainError, events::DomainEvent, ports::EventHandler};
|
|
||||||
|
|
||||||
use application::context::AppContext;
|
use async_trait::async_trait;
|
||||||
|
use domain::{
|
||||||
|
errors::DomainError,
|
||||||
|
events::DomainEvent,
|
||||||
|
ports::{EventHandler, PersonCommand, PersonEnrichmentClient, PersonQuery},
|
||||||
|
};
|
||||||
|
|
||||||
|
use application::person::deps::EnrichPersonDeps;
|
||||||
|
|
||||||
pub struct PersonEnrichmentHandler {
|
pub struct PersonEnrichmentHandler {
|
||||||
ctx: AppContext,
|
deps: EnrichPersonDeps,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PersonEnrichmentHandler {
|
impl PersonEnrichmentHandler {
|
||||||
pub fn new(ctx: AppContext) -> Self {
|
pub fn new(
|
||||||
Self { ctx }
|
person_query: Arc<dyn PersonQuery>,
|
||||||
|
person_enrichment: Option<Arc<dyn PersonEnrichmentClient>>,
|
||||||
|
person_command: Arc<dyn PersonCommand>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
deps: EnrichPersonDeps {
|
||||||
|
person_query,
|
||||||
|
person_enrichment,
|
||||||
|
person_command,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,6 +40,6 @@ impl EventHandler for PersonEnrichmentHandler {
|
|||||||
_ => return Ok(()),
|
_ => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
application::person::enrich::execute(&self.ctx, person_id, &external_person_id).await
|
application::person::enrich::execute(&self.deps, person_id, &external_person_id).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
crates/application/src/person/deps.rs
Normal file
14
crates/application/src/person/deps.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use domain::ports::{EventPublisher, PersonCommand, PersonEnrichmentClient, PersonQuery};
|
||||||
|
|
||||||
|
pub struct GetPersonDeps {
|
||||||
|
pub person_query: Arc<dyn PersonQuery>,
|
||||||
|
pub event_publisher: Arc<dyn EventPublisher>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EnrichPersonDeps {
|
||||||
|
pub person_query: Arc<dyn PersonQuery>,
|
||||||
|
pub person_enrichment: Option<Arc<dyn PersonEnrichmentClient>>,
|
||||||
|
pub person_command: Arc<dyn PersonCommand>,
|
||||||
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
use crate::context::AppContext;
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use domain::{errors::DomainError, models::PersonId};
|
use domain::{errors::DomainError, models::PersonId};
|
||||||
|
|
||||||
|
use super::deps::EnrichPersonDeps;
|
||||||
|
|
||||||
const STALENESS_DAYS: i64 = 90;
|
const STALENESS_DAYS: i64 = 90;
|
||||||
|
|
||||||
pub async fn execute(
|
pub async fn execute(
|
||||||
ctx: &AppContext,
|
deps: &EnrichPersonDeps,
|
||||||
person_id: PersonId,
|
person_id: PersonId,
|
||||||
external_id: &str,
|
external_id: &str,
|
||||||
) -> Result<(), DomainError> {
|
) -> Result<(), DomainError> {
|
||||||
if let Some(person) = ctx.repos.person_query.get_by_id(&person_id).await?
|
if let Some(person) = deps.person_query.get_by_id(&person_id).await?
|
||||||
&& let Some(at) = person.enriched_at()
|
&& let Some(at) = person.enriched_at()
|
||||||
&& (Utc::now() - at).num_days() < STALENESS_DAYS
|
&& (Utc::now() - at).num_days() < STALENESS_DAYS
|
||||||
{
|
{
|
||||||
@@ -17,7 +18,7 @@ pub async fn execute(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let client = ctx.services.person_enrichment.as_ref().ok_or_else(|| {
|
let client = deps.person_enrichment.as_ref().ok_or_else(|| {
|
||||||
DomainError::InfrastructureError("person enrichment client not configured".into())
|
DomainError::InfrastructureError("person enrichment client not configured".into())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@@ -30,8 +31,7 @@ pub async fn execute(
|
|||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.repos
|
deps.person_command
|
||||||
.person_command
|
|
||||||
.update_enrichment(&person_id, &data)
|
.update_enrichment(&person_id, &data)
|
||||||
.await?;
|
.await?;
|
||||||
tracing::info!(person_id = %person_id.value(), "person enriched");
|
tracing::info!(person_id = %person_id.value(), "person enriched");
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::context::AppContext;
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use domain::{
|
use domain::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
@@ -6,15 +5,16 @@ use domain::{
|
|||||||
models::{Person, PersonId},
|
models::{Person, PersonId},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::deps::GetPersonDeps;
|
||||||
|
|
||||||
const ENRICHMENT_TTL_DAYS: i64 = 90;
|
const ENRICHMENT_TTL_DAYS: i64 = 90;
|
||||||
|
|
||||||
pub async fn execute(ctx: &AppContext, id: PersonId) -> Result<Option<Person>, DomainError> {
|
pub async fn execute(deps: &GetPersonDeps, id: PersonId) -> Result<Option<Person>, DomainError> {
|
||||||
let person = ctx.repos.person_query.get_by_id(&id).await?;
|
let person = deps.person_query.get_by_id(&id).await?;
|
||||||
if let Some(ref p) = person
|
if let Some(ref p) = person
|
||||||
&& should_enrich(p)
|
&& should_enrich(p)
|
||||||
{
|
{
|
||||||
let _ = ctx
|
let _ = deps
|
||||||
.services
|
|
||||||
.event_publisher
|
.event_publisher
|
||||||
.publish(&DomainEvent::PersonEnrichmentRequested {
|
.publish(&DomainEvent::PersonEnrichmentRequested {
|
||||||
person_id: id,
|
person_id: id,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::context::AppContext;
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use domain::{
|
use domain::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
@@ -6,13 +5,14 @@ use domain::{
|
|||||||
models::{Person, PersonCredits, PersonId},
|
models::{Person, PersonCredits, PersonId},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::deps::GetPersonDeps;
|
||||||
|
|
||||||
const ENRICHMENT_TTL_DAYS: i64 = 90;
|
const ENRICHMENT_TTL_DAYS: i64 = 90;
|
||||||
|
|
||||||
pub async fn execute(ctx: &AppContext, id: PersonId) -> Result<PersonCredits, DomainError> {
|
pub async fn execute(deps: &GetPersonDeps, id: PersonId) -> Result<PersonCredits, DomainError> {
|
||||||
let credits = ctx.repos.person_query.get_credits(&id).await?;
|
let credits = deps.person_query.get_credits(&id).await?;
|
||||||
if should_enrich(&credits.person) {
|
if should_enrich(&credits.person) {
|
||||||
let _ = ctx
|
let _ = deps
|
||||||
.services
|
|
||||||
.event_publisher
|
.event_publisher
|
||||||
.publish(&DomainEvent::PersonEnrichmentRequested {
|
.publish(&DomainEvent::PersonEnrichmentRequested {
|
||||||
person_id: id,
|
person_id: id,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pub mod deps;
|
||||||
pub mod enrich;
|
pub mod enrich;
|
||||||
pub mod get;
|
pub mod get;
|
||||||
pub mod get_credits;
|
pub mod get_credits;
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
use domain::models::PersonId;
|
use domain::models::PersonId;
|
||||||
|
use domain::testing::{FakePersonQuery, NoopEventPublisher};
|
||||||
|
use std::sync::Arc;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::person::get;
|
use crate::person::{deps::GetPersonDeps, get};
|
||||||
use crate::test_helpers::TestContextBuilder;
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn returns_none_for_unknown_person() {
|
async fn returns_none_for_unknown_person() {
|
||||||
let ctx = TestContextBuilder::new().build();
|
let deps = GetPersonDeps {
|
||||||
|
person_query: Arc::new(FakePersonQuery),
|
||||||
|
event_publisher: NoopEventPublisher::new(),
|
||||||
|
};
|
||||||
|
|
||||||
let result = get::execute(&ctx, PersonId::from_uuid(Uuid::new_v4()))
|
let result = get::execute(&deps, PersonId::from_uuid(Uuid::new_v4()))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
use domain::models::PersonId;
|
use domain::models::PersonId;
|
||||||
|
use domain::testing::{FakePersonQuery, NoopEventPublisher};
|
||||||
|
use std::sync::Arc;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::person::get_credits;
|
use crate::person::{deps::GetPersonDeps, get_credits};
|
||||||
use crate::test_helpers::TestContextBuilder;
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn returns_empty_credits() {
|
async fn returns_empty_credits() {
|
||||||
let ctx = TestContextBuilder::new().build();
|
let deps = GetPersonDeps {
|
||||||
|
person_query: Arc::new(FakePersonQuery),
|
||||||
|
event_publisher: NoopEventPublisher::new(),
|
||||||
|
};
|
||||||
|
|
||||||
let result = get_credits::execute(&ctx, PersonId::from_uuid(Uuid::new_v4()))
|
let result = get_credits::execute(&deps, PersonId::from_uuid(Uuid::new_v4()))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use axum::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use application::{
|
use application::{
|
||||||
person::{get as get_person, get_credits as get_person_credits},
|
person::{deps::GetPersonDeps, get as get_person, get_credits as get_person_credits},
|
||||||
search::execute as search_uc,
|
search::execute as search_uc,
|
||||||
};
|
};
|
||||||
use domain::models::{PersonId, collections::PageParams};
|
use domain::models::{PersonId, collections::PageParams};
|
||||||
@@ -101,7 +101,11 @@ pub async fn get_person_handler(
|
|||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(id): Path<uuid::Uuid>,
|
Path(id): Path<uuid::Uuid>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
match get_person::execute(&state.app_ctx, PersonId::from_uuid(id)).await {
|
let deps = GetPersonDeps {
|
||||||
|
person_query: state.app_ctx.repos.person_query.clone(),
|
||||||
|
event_publisher: state.app_ctx.services.event_publisher.clone(),
|
||||||
|
};
|
||||||
|
match get_person::execute(&deps, PersonId::from_uuid(id)).await {
|
||||||
Ok(Some(person)) => axum::Json(PersonDto {
|
Ok(Some(person)) => axum::Json(PersonDto {
|
||||||
id: person.id().value(),
|
id: person.id().value(),
|
||||||
external_id: person.external_id().value().to_string(),
|
external_id: person.external_id().value().to_string(),
|
||||||
@@ -138,7 +142,11 @@ pub async fn get_person_credits_handler(
|
|||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
Path(id): Path<uuid::Uuid>,
|
Path(id): Path<uuid::Uuid>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
match get_person_credits::execute(&state.app_ctx, PersonId::from_uuid(id)).await {
|
let deps = GetPersonDeps {
|
||||||
|
person_query: state.app_ctx.repos.person_query.clone(),
|
||||||
|
event_publisher: state.app_ctx.services.event_publisher.clone(),
|
||||||
|
};
|
||||||
|
match get_person_credits::execute(&deps, PersonId::from_uuid(id)).await {
|
||||||
Ok(credits) => axum::Json(PersonCreditsDto {
|
Ok(credits) => axum::Json(PersonCreditsDto {
|
||||||
person: PersonDto {
|
person: PersonDto {
|
||||||
id: credits.person.id().value(),
|
id: credits.person.id().value(),
|
||||||
|
|||||||
@@ -147,11 +147,14 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
Arc::clone(&ctx.repos.search_command),
|
Arc::clone(&ctx.repos.search_command),
|
||||||
Arc::clone(&ctx.services.object_storage),
|
Arc::clone(&ctx.services.object_storage),
|
||||||
)) as Arc<dyn EventHandler>;
|
)) as Arc<dyn EventHandler>;
|
||||||
ctx.services.person_enrichment =
|
let person_enrichment_arc =
|
||||||
Some(Arc::clone(&client) as Arc<dyn PersonEnrichmentClient>);
|
Arc::clone(&client) as Arc<dyn PersonEnrichmentClient>;
|
||||||
let person_handler =
|
ctx.services.person_enrichment = Some(Arc::clone(&person_enrichment_arc));
|
||||||
Arc::new(tmdb_enrichment::PersonEnrichmentHandler::new(ctx.clone()))
|
let person_handler = Arc::new(tmdb_enrichment::PersonEnrichmentHandler::new(
|
||||||
as Arc<dyn EventHandler>;
|
Arc::clone(&ctx.repos.person_query),
|
||||||
|
Some(person_enrichment_arc),
|
||||||
|
Arc::clone(&ctx.repos.person_command),
|
||||||
|
)) as Arc<dyn EventHandler>;
|
||||||
let job = Arc::new(application::jobs::EnrichmentStalenessJob::new(ctx.clone()))
|
let job = Arc::new(application::jobs::EnrichmentStalenessJob::new(ctx.clone()))
|
||||||
as Arc<dyn PeriodicJob>;
|
as Arc<dyn PeriodicJob>;
|
||||||
(Some(handler), Some(person_handler), Some(job))
|
(Some(handler), Some(person_handler), Some(job))
|
||||||
|
|||||||
Reference in New Issue
Block a user