1use haste_fhir_operation_error::OperationOutcomeError;
14use haste_jwt::{ProjectId, TenantId, claims::UserTokenClaims};
15use serde::{Deserialize, Serialize};
16use sqlx::types::{Json, time::OffsetDateTime};
17use std::time::Duration;
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq, sqlx::Type, Serialize, Deserialize)]
21#[sqlx(type_name = "async_job_status", rename_all = "kebab-case")]
22#[serde(rename_all = "kebab-case")]
23pub enum AsyncJobStatus {
24 Accepted,
25 InProgress,
26 Completed,
27 Failed,
28 Cancelled,
29}
30
31impl AsyncJobStatus {
32 #[must_use]
33 pub fn as_str(&self) -> &'static str {
34 match self {
35 AsyncJobStatus::Accepted => "accepted",
36 AsyncJobStatus::InProgress => "in-progress",
37 AsyncJobStatus::Completed => "completed",
38 AsyncJobStatus::Failed => "failed",
39 AsyncJobStatus::Cancelled => "cancelled",
40 }
41 }
42
43 #[must_use]
45 pub fn is_live(&self) -> bool {
46 matches!(self, AsyncJobStatus::Accepted | AsyncJobStatus::InProgress)
47 }
48}
49
50impl std::fmt::Display for AsyncJobStatus {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 f.write_str(self.as_str())
53 }
54}
55
56#[derive(sqlx::FromRow, Clone, Debug)]
58pub struct AsyncJob {
59 pub id: String,
60 pub tenant: TenantId,
61 pub project: ProjectId,
62 pub kind: String,
63 pub status: AsyncJobStatus,
64 pub request: String,
65 pub parameters: Json<serde_json::Value>,
66 pub claims: Json<UserTokenClaims>,
68 pub submitted_by: String,
69 pub progress: Option<String>,
70 pub output: Option<Json<serde_json::Value>>,
71 pub output_content_type: Option<String>,
72 pub error: Option<Json<serde_json::Value>>,
74 pub attempts: i32,
75 pub max_attempts: i32,
76 pub locked_by: Option<String>,
77 pub lease_expires_at: Option<OffsetDateTime>,
78 pub created_at: OffsetDateTime,
79 pub updated_at: OffsetDateTime,
80 pub started_at: Option<OffsetDateTime>,
81 pub finished_at: Option<OffsetDateTime>,
82 pub expires_at: OffsetDateTime,
83}
84
85#[derive(Debug)]
87pub struct CreateAsyncJob {
88 pub kind: String,
89 pub request: String,
90 pub parameters: serde_json::Value,
91 pub claims: UserTokenClaims,
92 pub retention: Duration,
95 pub max_attempts: i32,
96}
97
98#[derive(Clone, Debug)]
100pub struct JobLease {
101 pub worker: String,
102 pub duration: Duration,
103}
104
105#[derive(Clone, Copy, Debug, PartialEq, Eq)]
107pub enum LeaseRenewal {
108 Renewed,
110 Lost,
113}
114
115#[derive(Clone, Debug)]
117pub enum JobResult {
118 Completed {
119 output: serde_json::Value,
120 content_type: String,
121 },
122 Failed {
123 outcome: serde_json::Value,
125 },
126}
127
128pub trait AsyncJobProvider {
129 fn create_job(
132 &self,
133 tenant: &TenantId,
134 project: &ProjectId,
135 job: CreateAsyncJob,
136 ) -> impl Future<Output = Result<AsyncJob, OperationOutcomeError>> + Send;
137
138 fn read_job(
140 &self,
141 tenant: &TenantId,
142 project: &ProjectId,
143 id: &str,
144 ) -> impl Future<Output = Result<Option<AsyncJob>, OperationOutcomeError>> + Send;
145
146 fn count_live_jobs(
148 &self,
149 tenant: &TenantId,
150 project: &ProjectId,
151 ) -> impl Future<Output = Result<i64, OperationOutcomeError>> + Send;
152
153 fn cancel_job(
158 &self,
159 tenant: &TenantId,
160 project: &ProjectId,
161 id: &str,
162 ) -> impl Future<Output = Result<Option<AsyncJob>, OperationOutcomeError>> + Send;
163
164 fn claim_job(
168 &self,
169 kinds: &[&str],
170 lease: &JobLease,
171 ) -> impl Future<Output = Result<Option<AsyncJob>, OperationOutcomeError>> + Send;
172
173 fn renew_lease(
176 &self,
177 id: &str,
178 lease: &JobLease,
179 progress: Option<&str>,
180 ) -> impl Future<Output = Result<LeaseRenewal, OperationOutcomeError>> + Send;
181
182 fn finish_job(
187 &self,
188 id: &str,
189 worker: &str,
190 result: JobResult,
191 retention: Duration,
192 ) -> impl Future<Output = Result<bool, OperationOutcomeError>> + Send;
193
194 fn fail_abandoned_jobs(
197 &self,
198 outcome: &serde_json::Value,
199 retention: Duration,
200 ) -> impl Future<Output = Result<u64, OperationOutcomeError>> + Send;
201
202 fn delete_expired_jobs(
205 &self,
206 limit: i64,
207 ) -> impl Future<Output = Result<Vec<AsyncJob>, OperationOutcomeError>> + Send;
208}