use super::RunError;
use chrono::{DateTime, Utc};
use dpm_model::{
ActorId, ActorKind, ContractObservation, LineageId, Observation, Plan, RunRecord, RunStart,
WorkStatus,
};
pub fn observe_start(
plan: &Plan,
lineage: LineageId,
writer: &ActorId,
request: &RunStart,
now: DateTime<Utc>,
) -> Result<RunRecord, RunError> {
request.validate()?;
super::transition::authorize_writer(writer, &request.executor, request.id, "start")?;
if request.observation == Observation::Managed && writer.kind != ActorKind::Service {
return Err(RunError::ManagedNeedsService(request.id));
}
let work = plan
.work_items
.get(&request.work)
.ok_or(RunError::MissingWork(request.work))?;
if !work.is_executable() {
return Err(RunError::NotATask(work.id));
}
if !matches!(
work.execution.status,
WorkStatus::Claimed | WorkStatus::InProgress
) {
return Err(RunError::NotExecuting {
work: work.id,
status: work.execution.status,
});
}
if work.execution.owner.as_ref() != Some(&request.executor) {
return Err(RunError::NotOwner {
work: work.id,
executor: request.executor.clone(),
owner: work.execution.owner.clone(),
});
}
let exact_sources = request
.sources
.iter()
.map(|source| super::source::resolve(plan, work, source))
.collect::<Result<Vec<_>, _>>()?;
Ok(RunRecord {
id: request.id,
work: work.id,
executor: request.executor.clone(),
recorded_by: writer.clone(),
parent: request.parent,
session: request.session.clone(),
observation: request.observation,
sources: request.sources.clone(),
exact_sources,
contract: ContractObservation {
workspace_id: plan.workspace.id,
lineage_id: lineage,
revision: plan.revision,
work_key: work.key.clone(),
prior_submissions: u32::try_from(work.execution.attempts.len()).unwrap_or(u32::MAX),
contract: work.contract.clone(),
},
observed_started_at: request.observed_at,
started_at: now,
})
}