app: person enrichment use case + staleness checks
This commit is contained in:
@@ -3,11 +3,11 @@ use std::sync::Arc;
|
|||||||
use domain::ports::{
|
use domain::ports::{
|
||||||
AuthService, DiaryExporter, DiaryRepository, DocumentParser, EventPublisher, GoalRepository,
|
AuthService, DiaryExporter, DiaryRepository, DocumentParser, EventPublisher, GoalRepository,
|
||||||
ImportProfileRepository, ImportSessionRepository, MetadataClient, MovieProfileRepository,
|
ImportProfileRepository, ImportSessionRepository, MetadataClient, MovieProfileRepository,
|
||||||
MovieRepository, ObjectStorage, PasswordHasher, PersonCommand, PersonQuery,
|
MovieRepository, ObjectStorage, PasswordHasher, PersonCommand, PersonEnrichmentClient,
|
||||||
PosterFetcherClient, RemoteGoalRepository, RemoteWatchlistRepository, ReviewRepository,
|
PersonQuery, PosterFetcherClient, RemoteGoalRepository, RemoteWatchlistRepository,
|
||||||
SearchCommand, SearchPort, SocialQueryPort, StatsRepository, UserProfileFieldsRepository,
|
ReviewRepository, SearchCommand, SearchPort, SocialQueryPort, StatsRepository,
|
||||||
UserRepository, UserSettingsRepository, WatchEventRepository, WatchlistRepository,
|
UserProfileFieldsRepository, UserRepository, UserSettingsRepository, WatchEventRepository,
|
||||||
WebhookTokenRepository, WrapUpRepository, WrapUpStatsQuery,
|
WatchlistRepository, WebhookTokenRepository, WrapUpRepository, WrapUpStatsQuery,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::config::AppConfig;
|
use crate::config::AppConfig;
|
||||||
@@ -51,6 +51,7 @@ pub struct Services {
|
|||||||
pub diary_exporter: Arc<dyn DiaryExporter>,
|
pub diary_exporter: Arc<dyn DiaryExporter>,
|
||||||
pub document_parser: Arc<dyn DocumentParser>,
|
pub document_parser: Arc<dyn DocumentParser>,
|
||||||
pub review_logger: Arc<dyn ReviewLogger>,
|
pub review_logger: Arc<dyn ReviewLogger>,
|
||||||
|
pub person_enrichment: Option<Arc<dyn PersonEnrichmentClient>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|||||||
13
crates/application/src/person/enrich.rs
Normal file
13
crates/application/src/person/enrich.rs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
use crate::context::AppContext;
|
||||||
|
use domain::{
|
||||||
|
errors::DomainError,
|
||||||
|
models::{PersonEnrichmentData, PersonId},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn execute(
|
||||||
|
ctx: &AppContext,
|
||||||
|
person_id: PersonId,
|
||||||
|
data: PersonEnrichmentData,
|
||||||
|
) -> Result<(), DomainError> {
|
||||||
|
ctx.repos.person_command.update_enrichment(&person_id, &data).await
|
||||||
|
}
|
||||||
@@ -1,11 +1,33 @@
|
|||||||
use crate::context::AppContext;
|
use crate::context::AppContext;
|
||||||
|
use chrono::Utc;
|
||||||
use domain::{
|
use domain::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
|
events::DomainEvent,
|
||||||
models::{Person, PersonId},
|
models::{Person, PersonId},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ENRICHMENT_TTL_DAYS: i64 = 90;
|
||||||
|
|
||||||
pub async fn execute(ctx: &AppContext, id: PersonId) -> Result<Option<Person>, DomainError> {
|
pub async fn execute(ctx: &AppContext, id: PersonId) -> Result<Option<Person>, DomainError> {
|
||||||
ctx.repos.person_query.get_by_id(&id).await
|
let person = ctx.repos.person_query.get_by_id(&id).await?;
|
||||||
|
if let Some(ref p) = person {
|
||||||
|
if should_enrich(p) {
|
||||||
|
let _ = ctx.services.event_publisher.publish(
|
||||||
|
&DomainEvent::PersonEnrichmentRequested {
|
||||||
|
person_id: id,
|
||||||
|
external_person_id: p.external_id().value().to_string(),
|
||||||
|
},
|
||||||
|
).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(person)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_enrich(p: &Person) -> bool {
|
||||||
|
match p.enriched_at() {
|
||||||
|
None => true,
|
||||||
|
Some(at) => (Utc::now() - at).num_days() >= ENRICHMENT_TTL_DAYS,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -1,11 +1,31 @@
|
|||||||
use crate::context::AppContext;
|
use crate::context::AppContext;
|
||||||
|
use chrono::Utc;
|
||||||
use domain::{
|
use domain::{
|
||||||
errors::DomainError,
|
errors::DomainError,
|
||||||
models::{PersonCredits, PersonId},
|
events::DomainEvent,
|
||||||
|
models::{Person, PersonCredits, PersonId},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ENRICHMENT_TTL_DAYS: i64 = 90;
|
||||||
|
|
||||||
pub async fn execute(ctx: &AppContext, id: PersonId) -> Result<PersonCredits, DomainError> {
|
pub async fn execute(ctx: &AppContext, id: PersonId) -> Result<PersonCredits, DomainError> {
|
||||||
ctx.repos.person_query.get_credits(&id).await
|
let credits = ctx.repos.person_query.get_credits(&id).await?;
|
||||||
|
if should_enrich(&credits.person) {
|
||||||
|
let _ = ctx.services.event_publisher.publish(
|
||||||
|
&DomainEvent::PersonEnrichmentRequested {
|
||||||
|
person_id: id,
|
||||||
|
external_person_id: credits.person.external_id().value().to_string(),
|
||||||
|
},
|
||||||
|
).await;
|
||||||
|
}
|
||||||
|
Ok(credits)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_enrich(p: &Person) -> bool {
|
||||||
|
match p.enriched_at() {
|
||||||
|
None => true,
|
||||||
|
Some(at) => (Utc::now() - at).num_days() >= ENRICHMENT_TTL_DAYS,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
|
pub mod enrich;
|
||||||
pub mod get;
|
pub mod get;
|
||||||
pub mod get_credits;
|
pub mod get_credits;
|
||||||
|
|||||||
@@ -297,6 +297,7 @@ impl TestContextBuilder {
|
|||||||
diary_exporter: self.diary_exporter,
|
diary_exporter: self.diary_exporter,
|
||||||
document_parser: self.document_parser,
|
document_parser: self.document_parser,
|
||||||
review_logger: self.review_logger,
|
review_logger: self.review_logger,
|
||||||
|
person_enrichment: None,
|
||||||
},
|
},
|
||||||
config: self.config,
|
config: self.config,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,6 +218,7 @@ async fn wire_dependencies() -> anyhow::Result<(AppState, axum::Router)> {
|
|||||||
diary_exporter: Arc::new(ExportAdapter) as Arc<dyn DiaryExporter>,
|
diary_exporter: Arc::new(ExportAdapter) as Arc<dyn DiaryExporter>,
|
||||||
document_parser: Arc::new(ImporterDocumentParser) as Arc<dyn DocumentParser>,
|
document_parser: Arc::new(ImporterDocumentParser) as Arc<dyn DocumentParser>,
|
||||||
review_logger,
|
review_logger,
|
||||||
|
person_enrichment: None,
|
||||||
},
|
},
|
||||||
config: app_config,
|
config: app_config,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -781,6 +781,7 @@ pub fn make_test_state(auth_service: Arc<dyn AuthService>) -> crate::state::AppS
|
|||||||
diary_exporter: Arc::clone(&repo) as _,
|
diary_exporter: Arc::clone(&repo) as _,
|
||||||
document_parser: Arc::clone(&repo) as _,
|
document_parser: Arc::clone(&repo) as _,
|
||||||
review_logger: Arc::clone(&repo) as _,
|
review_logger: Arc::clone(&repo) as _,
|
||||||
|
person_enrichment: None,
|
||||||
},
|
},
|
||||||
config: AppConfig {
|
config: AppConfig {
|
||||||
allow_registration: false,
|
allow_registration: false,
|
||||||
|
|||||||
@@ -470,6 +470,7 @@ async fn test_app() -> Router {
|
|||||||
diary_exporter: Arc::new(PanicExporter),
|
diary_exporter: Arc::new(PanicExporter),
|
||||||
document_parser: Arc::new(PanicDocumentParser),
|
document_parser: Arc::new(PanicDocumentParser),
|
||||||
review_logger: Arc::new(PanicReviewLogger),
|
review_logger: Arc::new(PanicReviewLogger),
|
||||||
|
person_enrichment: None,
|
||||||
},
|
},
|
||||||
config: AppConfig {
|
config: AppConfig {
|
||||||
allow_registration: false,
|
allow_registration: false,
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
diary_exporter: Arc::new(ExportAdapter) as Arc<dyn DiaryExporter>,
|
diary_exporter: Arc::new(ExportAdapter) as Arc<dyn DiaryExporter>,
|
||||||
document_parser: Arc::new(ImporterDocumentParser) as Arc<dyn DocumentParser>,
|
document_parser: Arc::new(ImporterDocumentParser) as Arc<dyn DocumentParser>,
|
||||||
review_logger,
|
review_logger,
|
||||||
|
person_enrichment: None,
|
||||||
},
|
},
|
||||||
config: app_config,
|
config: app_config,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user