refactor: store typed WrapUpReport in domain, serialize in adapters
Some checks failed
CI / Check / Test (push) Failing after 45s

This commit is contained in:
2026-06-03 01:24:02 +02:00
parent e4b8ba550e
commit d94ccbe057
9 changed files with 33 additions and 32 deletions

View File

@@ -4,7 +4,7 @@ use async_trait::async_trait;
use chrono::NaiveDate;
use domain::{
errors::DomainError,
models::wrapup::{DateRange, WrapUpRecord, WrapUpScope, WrapUpStatus},
models::wrapup::{DateRange, WrapUpRecord, WrapUpReport, WrapUpScope, WrapUpStatus},
ports::{WrapUpMovieRow, WrapUpRepository, WrapUpStatsQuery},
value_objects::WrapUpId,
};
@@ -68,7 +68,7 @@ impl WrapUpRepository for PostgresWrapUpRepository {
.bind(record.start_date)
.bind(record.end_date)
.bind(status)
.bind(&record.report_json)
.bind(record.report.as_ref().and_then(|r| serde_json::to_string(r).ok()))
.bind(&record.error_message)
.bind(record.created_at)
.bind(record.completed_at)
@@ -99,15 +99,17 @@ impl WrapUpRepository for PostgresWrapUpRepository {
Ok(())
}
async fn set_complete(&self, id: &WrapUpId, report_json: &str) -> Result<(), DomainError> {
async fn set_complete(&self, id: &WrapUpId, report: &WrapUpReport) -> Result<(), DomainError> {
let id_str = id.value().to_string();
let json = serde_json::to_string(report)
.map_err(|e| DomainError::InfrastructureError(e.to_string()))?;
sqlx::query(
"UPDATE wrap_up_records \
SET status = 'ready', report_json = $1, completed_at = NOW() \
WHERE id = $2",
)
.bind(report_json)
.bind(&json)
.bind(&id_str)
.execute(&self.pool)
.await
@@ -227,13 +229,15 @@ fn row_to_record(row: &sqlx::postgres::PgRow) -> Result<WrapUpRecord, DomainErro
let user_id = user_id_str.as_deref().map(parse_uuid).transpose()?;
let report = report_json.and_then(|j| serde_json::from_str(&j).ok());
Ok(WrapUpRecord {
id: WrapUpId::from_uuid(parse_uuid(&id_str)?),
user_id,
start_date,
end_date,
status: parse_status(&status_str)?,
report_json,
report,
error_message,
created_at: parse_datetime(&created_at_str)?,
completed_at: completed_at_str

View File

@@ -4,7 +4,7 @@ use async_trait::async_trait;
use chrono::NaiveDate;
use domain::{
errors::DomainError,
models::wrapup::{DateRange, WrapUpRecord, WrapUpScope, WrapUpStatus},
models::wrapup::{DateRange, WrapUpRecord, WrapUpReport, WrapUpScope, WrapUpStatus},
ports::{WrapUpMovieRow, WrapUpRepository, WrapUpStatsQuery},
value_objects::WrapUpId,
};
@@ -79,7 +79,7 @@ impl WrapUpRepository for SqliteWrapUpRepository {
.bind(&start)
.bind(&end)
.bind(status)
.bind(&record.report_json)
.bind(record.report.as_ref().and_then(|r| serde_json::to_string(r).ok()))
.bind(&record.error_message)
.bind(&created)
.bind(&completed)
@@ -110,15 +110,17 @@ impl WrapUpRepository for SqliteWrapUpRepository {
Ok(())
}
async fn set_complete(&self, id: &WrapUpId, report_json: &str) -> Result<(), DomainError> {
async fn set_complete(&self, id: &WrapUpId, report: &WrapUpReport) -> Result<(), DomainError> {
let id_str = id.value().to_string();
let json = serde_json::to_string(report)
.map_err(|e| DomainError::InfrastructureError(e.to_string()))?;
sqlx::query(
"UPDATE wrap_up_records \
SET status = 'ready', report_json = ?, completed_at = strftime('%Y-%m-%d %H:%M:%S', 'now') \
WHERE id = ?",
)
.bind(report_json)
.bind(&json)
.bind(&id_str)
.execute(&self.pool)
.await
@@ -238,13 +240,15 @@ fn row_to_record(row: &sqlx::sqlite::SqliteRow) -> Result<WrapUpRecord, DomainEr
let user_id = user_id_str.as_deref().map(parse_uuid).transpose()?;
let report = report_json.and_then(|j| serde_json::from_str(&j).ok());
Ok(WrapUpRecord {
id: WrapUpId::from_uuid(parse_uuid(&id_str)?),
user_id,
start_date: parse_date(&start_date_str)?,
end_date: parse_date(&end_date_str)?,
status: parse_status(&status_str)?,
report_json,
report,
error_message,
created_at: parse_datetime(&created_at_str)?,
completed_at: completed_at_str

View File

@@ -39,7 +39,7 @@ pub async fn execute(ctx: &AppContext, cmd: RequestWrapUpCommand) -> Result<Wrap
start_date: date_range.start(),
end_date: date_range.end(),
status: WrapUpStatus::Pending,
report_json: None,
report: None,
error_message: None,
created_at: Utc::now().naive_utc(),
completed_at: None,

View File

@@ -40,11 +40,9 @@ pub async fn execute(
match compute::execute(ctx, query).await {
Ok(report) => {
let json = serde_json::to_string(&report)
.map_err(|e| DomainError::InfrastructureError(e.to_string()))?;
ctx.repos
.wrapup_repo
.set_complete(&wrapup_id, &json)
.set_complete(&wrapup_id, &report)
.await?;
if let Some(ref renderer) = ctx.services.video_renderer {

View File

@@ -160,7 +160,7 @@ pub struct WrapUpRecord {
pub start_date: NaiveDate,
pub end_date: NaiveDate,
pub status: WrapUpStatus,
pub report_json: Option<String>,
pub report: Option<WrapUpReport>,
pub error_message: Option<String>,
pub created_at: NaiveDateTime,
pub completed_at: Option<NaiveDateTime>,

View File

@@ -485,7 +485,7 @@ pub trait WrapUpRepository: Send + Sync {
status: &WrapUpStatus,
error: Option<&str>,
) -> Result<(), DomainError>;
async fn set_complete(&self, id: &WrapUpId, report_json: &str) -> Result<(), DomainError>;
async fn set_complete(&self, id: &WrapUpId, report: &WrapUpReport) -> Result<(), DomainError>;
async fn get_by_id(&self, id: &WrapUpId) -> Result<Option<WrapUpRecord>, DomainError>;
async fn list_for_user(&self, user_id: Uuid) -> Result<Vec<WrapUpRecord>, DomainError>;
async fn list_global(&self) -> Result<Vec<WrapUpRecord>, DomainError>;

View File

@@ -1099,11 +1099,11 @@ impl WrapUpRepository for InMemoryWrapUpRepository {
}
}
async fn set_complete(&self, id: &WrapUpId, report_json: &str) -> Result<(), DomainError> {
async fn set_complete(&self, id: &WrapUpId, report: &crate::models::wrapup::WrapUpReport) -> Result<(), DomainError> {
let mut store = self.store.lock().unwrap();
if let Some(rec) = store.iter_mut().find(|r| r.id == *id) {
rec.status = crate::models::wrapup::WrapUpStatus::Ready;
rec.report_json = Some(report_json.to_string());
rec.report = Some(report.clone());
rec.completed_at = Some(chrono::Utc::now().naive_utc());
Ok(())
} else {
@@ -1189,7 +1189,7 @@ impl WrapUpRepository for PanicWrapUpRepository {
) -> Result<(), DomainError> {
panic!("PanicWrapUpRepository called")
}
async fn set_complete(&self, _: &WrapUpId, _: &str) -> Result<(), DomainError> {
async fn set_complete(&self, _: &WrapUpId, _: &crate::models::wrapup::WrapUpReport) -> Result<(), DomainError> {
panic!("PanicWrapUpRepository called")
}
async fn get_by_id(

View File

@@ -134,8 +134,9 @@ pub async fn get_report(
Path(id): Path<Uuid>,
) -> impl IntoResponse {
match get_wrapup::execute(&state.app_ctx, WrapUpId::from_uuid(id)).await {
Ok(Some(record)) if record.status == WrapUpStatus::Ready => match record.report_json {
Some(json) => {
Ok(Some(record)) if record.status == WrapUpStatus::Ready => match record.report {
Some(ref report) => {
let json = serde_json::to_string(report).unwrap_or_default();
(StatusCode::OK, [("content-type", "application/json")], json).into_response()
}
None => StatusCode::NOT_FOUND.into_response(),
@@ -295,11 +296,8 @@ pub async fn get_user_wrapup_html(
_ => return StatusCode::NOT_FOUND.into_response(),
};
let report: WrapUpReport = match &record.report_json {
Some(json) => match serde_json::from_str(json) {
Ok(r) => r,
Err(_) => return StatusCode::INTERNAL_SERVER_ERROR.into_response(),
},
let report = match record.report {
Some(r) => r,
None => return StatusCode::NOT_FOUND.into_response(),
};
@@ -334,11 +332,8 @@ pub async fn get_global_wrapup_html(
_ => return StatusCode::NOT_FOUND.into_response(),
};
let report: WrapUpReport = match &record.report_json {
Some(json) => match serde_json::from_str(json) {
Ok(r) => r,
Err(_) => return StatusCode::INTERNAL_SERVER_ERROR.into_response(),
},
let report = match record.report {
Some(r) => r,
None => return StatusCode::NOT_FOUND.into_response(),
};

View File

@@ -602,7 +602,7 @@ impl domain::ports::WrapUpRepository for Panic {
async fn set_complete(
&self,
_: &domain::value_objects::WrapUpId,
_: &str,
_: &domain::models::wrapup::WrapUpReport,
) -> Result<(), DomainError> {
panic!()
}