use std::collections::HashMap;
use aion_core::{
OutstandingLease, PackageVersion, RunId, SearchAttributeValue, WorkflowId, WorkflowListRequest,
WorkflowStatus, WorkflowSummary,
};
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::StoreError;
#[async_trait]
pub trait VisibilityStore: Send + Sync + 'static {
async fn record_visibility(&self, record: VisibilityRecord) -> Result<(), StoreError>;
async fn get_visibility(
&self,
workflow_id: &WorkflowId,
) -> Result<Option<VisibilityRecord>, StoreError>;
async fn remove_visibility(
&self,
workflow_id: &WorkflowId,
run_id: &RunId,
) -> Result<bool, StoreError>;
async fn list_workflows(
&self,
request: &WorkflowListRequest,
) -> Result<VisibilityPage, StoreError>;
}
#[derive(Clone, Debug, PartialEq)]
pub struct VisibilityPage {
pub items: Vec<VisibilityRecord>,
pub next_cursor: Option<String>,
pub count: u64,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct VisibilityRecord {
pub namespace: String,
pub workflow_id: WorkflowId,
pub run_id: RunId,
pub workflow_type: String,
pub status: WorkflowStatus,
pub started_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub ended_at: Option<DateTime<Utc>>,
pub parent: Option<WorkflowId>,
pub display_name: Option<String>,
pub kind: Option<String>,
pub failed_step: Option<String>,
pub failure_reason: Option<String>,
pub search_attributes: HashMap<String, SearchAttributeValue>,
#[serde(default)]
pub outstanding_leases: Vec<OutstandingLease>,
#[serde(default)]
pub package_version: Option<PackageVersion>,
#[serde(default)]
pub head_seq: u64,
}
impl VisibilityRecord {
#[must_use]
pub fn summary(&self) -> WorkflowSummary {
WorkflowSummary {
workflow_id: self.workflow_id.clone(),
run_id: self.run_id.clone(),
workflow_type: self.workflow_type.clone(),
status: self.status,
started_at: self.started_at,
updated_at: self.updated_at,
ended_at: self.ended_at,
parent: self.parent.clone(),
failed_step: self.failed_step.clone(),
failure_reason: self.failure_reason.clone(),
display_name: self.display_name.clone(),
kind: self.kind.clone(),
current_worker: aion_core::current_worker(&self.outstanding_leases),
package_version: self.package_version.clone(),
}
}
}
impl From<VisibilityRecord> for WorkflowSummary {
fn from(record: VisibilityRecord) -> Self {
Self {
workflow_id: record.workflow_id,
run_id: record.run_id,
workflow_type: record.workflow_type,
status: record.status,
started_at: record.started_at,
updated_at: record.updated_at,
ended_at: record.ended_at,
parent: record.parent,
failed_step: record.failed_step,
failure_reason: record.failure_reason,
display_name: record.display_name,
kind: record.kind,
current_worker: aion_core::current_worker(&record.outstanding_leases),
package_version: record.package_version,
}
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use aion_core::{PackageVersion, WorkflowSummary};
use super::{VisibilityRecord, VisibilityStore};
#[test]
fn visibility_store_is_object_safe() {
let _: Option<Arc<dyn VisibilityStore>> = None;
}
#[test]
fn a_row_written_before_the_package_field_decodes_as_not_carried()
-> Result<(), Box<dyn std::error::Error>> {
let legacy = serde_json::json!({
"namespace": "default",
"workflow_id": "00000000-0000-0000-0000-000000000001",
"run_id": "00000000-0000-0000-0000-000000000002",
"workflow_type": "checkout",
"status": "Running",
"started_at": "2023-11-14T22:13:21Z",
"updated_at": "2023-11-14T22:13:21Z",
"ended_at": null,
"parent": null,
"display_name": null,
"kind": null,
"failed_step": null,
"failure_reason": null,
"search_attributes": {}
});
let record: VisibilityRecord = serde_json::from_value(legacy.clone())?;
assert_eq!(record.package_version, None);
assert_eq!(record.summary().package_version, None);
let mut carried = legacy;
carried["package_version"] = serde_json::Value::String("c".repeat(64));
let record: VisibilityRecord = serde_json::from_value(carried)?;
assert_eq!(
record.summary().package_version,
Some(PackageVersion::new("c".repeat(64)))
);
assert_eq!(
WorkflowSummary::from(record).package_version,
Some(PackageVersion::new("c".repeat(64)))
);
Ok(())
}
}