use std::sync::Arc;
use async_trait::async_trait;
use serde_json::Value;
use crate::case::{CaseStore, Correlation};
use crate::core::{
Case, CaseId, CaseStatus, CaseVersion, CorrelationKey, Deadline, DeadlineState, Digest, RunId,
StoreError, TenantId, Timestamp,
};
use crate::journal::payload;
use super::KeyRing;
#[derive(Debug)]
pub struct SealedCases {
inner: Arc<dyn CaseStore>,
keys: Arc<dyn KeyRing>,
tenant: TenantId,
}
impl SealedCases {
#[must_use]
pub fn wrap(inner: Arc<dyn CaseStore>, keys: Arc<dyn KeyRing>, tenant: TenantId) -> Arc<Self> {
super::assert_serves(inner.tenant(), &tenant, "case");
Arc::new(Self {
inner,
keys,
tenant,
})
}
fn scope_for(&self, case: CaseId) -> String {
super::scope(&self.tenant, &case.to_string())
}
fn aad(&self, case: CaseId) -> String {
format!("case-state:{}:{case}", self.tenant)
}
async fn open_state(&self, case: CaseId, state: Value) -> Value {
let Some(envelope) = payload::unwrap(&state) else {
return state;
};
let aad = self.aad(case);
match super::envelope::open(self.keys.as_ref(), aad.as_bytes(), &envelope).await {
Ok(plain) => serde_json::from_slice(&plain).unwrap_or(state),
Err(_) => state,
}
}
async fn opened(&self, case: Option<Case>) -> Option<Case> {
let mut case = case?;
case.state = self
.open_state(case.id, std::mem::take(&mut case.state))
.await;
Some(case)
}
}
pub async fn probe_sealed_case_state(
keys: &dyn KeyRing,
case: CaseId,
state: &Value,
) -> Option<Result<(), super::KeyError>> {
let envelope = payload::unwrap(state)?;
let scope = super::envelope::wrapped_scope(&envelope)?;
let tenant = scope.strip_suffix(&format!("/{case}"))?;
let aad = format!("case-state:{tenant}:{case}");
Some(
super::envelope::open(keys, aad.as_bytes(), &envelope)
.await
.map(drop),
)
}
#[async_trait]
impl CaseStore for SealedCases {
fn tenant(&self) -> &str {
self.tenant.as_str()
}
async fn put_state(
&self,
case: CaseId,
expected: CaseVersion,
state: Value,
) -> Result<CaseVersion, StoreError> {
let plain = crate::core::canon::to_bytes(&state)
.map_err(|e| StoreError::Backend(format!("case state would not serialise: {e}")))?;
let envelope = super::envelope::seal(
self.keys.as_ref(),
&self.scope_for(case),
self.aad(case).as_bytes(),
&plain,
)
.await
.map_err(|e| StoreError::Backend(format!("sealing case state failed: {e}")))?;
self.inner
.put_state(case, expected, payload::wrap(&envelope))
.await
}
async fn case(&self, id: CaseId) -> Result<Option<Case>, StoreError> {
let found = self.inner.case(id).await?;
Ok(self.opened(found).await)
}
async fn by_status(&self, status: CaseStatus, limit: usize) -> Result<Vec<Case>, StoreError> {
let cases = self.inner.by_status(status, limit).await?;
let mut out = Vec::with_capacity(cases.len());
for case in cases {
if let Some(opened) = self.opened(Some(case)).await {
out.push(opened);
}
}
Ok(out)
}
async fn correlate(&self, keys: &[CorrelationKey]) -> Result<Option<CaseId>, StoreError> {
self.inner.correlate(keys).await
}
async fn correlate_or_open(
&self,
kind: &str,
keys: &[CorrelationKey],
at: Timestamp,
) -> Result<Correlation, StoreError> {
self.inner.correlate_or_open(kind, keys, at).await
}
async fn cases(&self, after: Option<CaseId>, limit: usize) -> Result<Vec<Case>, StoreError> {
self.inner.cases(after, limit).await
}
async fn import_case(
&self,
case: &Case,
deadlines: &[crate::core::Deadline],
blobs: &[crate::core::Digest],
) -> Result<(), StoreError> {
self.inner.import_case(case, deadlines, blobs).await
}
async fn attach_run(&self, case: CaseId, run: RunId) -> Result<(), StoreError> {
self.inner.attach_run(case, run).await
}
async fn link_blob(
&self,
case: CaseId,
digest: Digest,
at: Timestamp,
) -> Result<(), StoreError> {
self.inner.link_blob(case, digest, at).await
}
async fn blobs_of(&self, case: CaseId) -> Result<Vec<Digest>, StoreError> {
self.inner.blobs_of(case).await
}
async fn set_status(&self, case: CaseId, status: CaseStatus) -> Result<(), StoreError> {
self.inner.set_status(case, status).await
}
async fn close(&self, case: CaseId) -> Result<(), StoreError> {
self.inner.close(case).await
}
async fn register_deadline(&self, deadline: &Deadline) -> Result<(), StoreError> {
self.inner.register_deadline(deadline).await
}
async fn deadlines(&self, case: CaseId) -> Result<Vec<Deadline>, StoreError> {
self.inner.deadlines(case).await
}
async fn set_deadline_state(
&self,
case: CaseId,
name: &str,
state: DeadlineState,
) -> Result<(), StoreError> {
self.inner.set_deadline_state(case, name, state).await
}
async fn due(&self, now: Timestamp, limit: usize) -> Result<Vec<Deadline>, StoreError> {
self.inner.due(now, limit).await
}
async fn census(&self, now: Timestamp) -> Result<crate::case::CaseCensus, StoreError> {
self.inner.census(now).await
}
}