use async_trait::async_trait;
use crate::Postgres;
use crate::reconcile::TenantRecord;
use cratefield_core::Database as _;
use cratefield_core::{
DbError, Statement, TenantLifecycle, TenantLifecycleError, TenantStatus, TenantSummary,
};
impl Postgres {
pub async fn tenant_status(&self, tenant: &str) -> Result<Option<TenantStatus>, DbError> {
let rows = self
.query(&Statement::with_values(
"SELECT status FROM harness_tenants WHERE tenant = ?".to_owned(),
vec![tenant.to_owned().into()],
))
.await?;
Ok(rows.rows.iter().find_map(|row| {
row.get::<String>("status")
.map(|status| TenantStatus::parse(&status))
}))
}
}
#[async_trait]
impl TenantLifecycle for Postgres {
async fn create(&self, tenant: &str, dsn: &str) -> Result<(), TenantLifecycleError> {
if let Err(error) = self.register_tenant(tenant, dsn).await {
let archived = matches!(
self.tenant_status(tenant).await,
Ok(Some(TenantStatus::Archived))
);
return if archived {
Err(TenantLifecycleError::Refused {
tenant: tenant.to_owned(),
from: TenantStatus::Archived,
to: TenantStatus::Provisioning,
})
} else {
Err(TenantLifecycleError::Backend {
tenant: tenant.to_owned(),
message: error.to_string(),
})
};
}
Ok(())
}
async fn status(&self, tenant: &str) -> Result<TenantStatus, TenantLifecycleError> {
match self.tenant_status(tenant).await {
Ok(Some(status)) => Ok(status),
Ok(None) => Err(TenantLifecycleError::Unknown {
tenant: tenant.to_owned(),
}),
Err(error) => Err(TenantLifecycleError::Backend {
tenant: tenant.to_owned(),
message: error.to_string(),
}),
}
}
async fn tenants(&self) -> Result<Vec<TenantSummary>, TenantLifecycleError> {
let records: Vec<TenantRecord> = Postgres::tenants(self).await.map_err(|error| {
TenantLifecycleError::Backend {
tenant: String::new(),
message: error.to_string(),
}
})?;
Ok(records
.into_iter()
.map(|record| TenantSummary {
tenant: record.tenant,
status: record.status,
})
.collect())
}
async fn set_status(
&self,
tenant: &str,
next: TenantStatus,
) -> Result<(), TenantLifecycleError> {
if self.set_tenant_status(tenant, next).await {
return Ok(());
}
match self.tenant_status(tenant).await {
Ok(None) => Err(TenantLifecycleError::Unknown {
tenant: tenant.to_owned(),
}),
Ok(Some(current)) => Err(TenantLifecycleError::Refused {
tenant: tenant.to_owned(),
from: current,
to: next,
}),
Err(error) => Err(TenantLifecycleError::Backend {
tenant: tenant.to_owned(),
message: error.to_string(),
}),
}
}
}