use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MockEnvironmentName {
Dev,
Test,
Prod,
}
impl MockEnvironmentName {
pub fn as_str(&self) -> &'static str {
match self {
MockEnvironmentName::Dev => "dev",
MockEnvironmentName::Test => "test",
MockEnvironmentName::Prod => "prod",
}
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"dev" => Some(MockEnvironmentName::Dev),
"test" => Some(MockEnvironmentName::Test),
"prod" => Some(MockEnvironmentName::Prod),
_ => None,
}
}
}
impl std::fmt::Display for MockEnvironmentName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, FromRow, Serialize, Deserialize)]
pub struct MockEnvironment {
pub id: Uuid,
pub workspace_id: Uuid,
pub name: String, pub reality_config: serde_json::Value,
pub chaos_config: serde_json::Value,
pub drift_budget_config: serde_json::Value,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[cfg(feature = "postgres")]
impl MockEnvironment {
pub fn environment_name(&self) -> Option<MockEnvironmentName> {
MockEnvironmentName::from_str(&self.name)
}
pub async fn create(
pool: &sqlx::PgPool,
workspace_id: Uuid,
name: MockEnvironmentName,
reality_config: Option<serde_json::Value>,
chaos_config: Option<serde_json::Value>,
drift_budget_config: Option<serde_json::Value>,
) -> sqlx::Result<Self> {
sqlx::query_as::<_, Self>(
r#"
INSERT INTO mock_environments (
workspace_id, name, reality_config, chaos_config, drift_budget_config
)
VALUES ($1, $2, $3, $4, $5)
RETURNING *
"#,
)
.bind(workspace_id)
.bind(name.as_str())
.bind(reality_config.unwrap_or_else(|| serde_json::json!({})))
.bind(chaos_config.unwrap_or_else(|| serde_json::json!({})))
.bind(drift_budget_config.unwrap_or_else(|| serde_json::json!({})))
.fetch_one(pool)
.await
}
pub async fn find_by_id(pool: &sqlx::PgPool, id: Uuid) -> sqlx::Result<Option<Self>> {
sqlx::query_as::<_, Self>("SELECT * FROM mock_environments WHERE id = $1")
.bind(id)
.fetch_optional(pool)
.await
}
pub async fn find_by_workspace_and_name(
pool: &sqlx::PgPool,
workspace_id: Uuid,
name: MockEnvironmentName,
) -> sqlx::Result<Option<Self>> {
sqlx::query_as::<_, Self>(
"SELECT * FROM mock_environments WHERE workspace_id = $1 AND name = $2",
)
.bind(workspace_id)
.bind(name.as_str())
.fetch_optional(pool)
.await
}
pub async fn list_by_workspace(
pool: &sqlx::PgPool,
workspace_id: Uuid,
) -> sqlx::Result<Vec<Self>> {
sqlx::query_as::<_, Self>(
"SELECT * FROM mock_environments WHERE workspace_id = $1 ORDER BY name",
)
.bind(workspace_id)
.fetch_all(pool)
.await
}
pub async fn update_config(
&self,
pool: &sqlx::PgPool,
reality_config: Option<serde_json::Value>,
chaos_config: Option<serde_json::Value>,
drift_budget_config: Option<serde_json::Value>,
) -> sqlx::Result<Self> {
sqlx::query_as::<_, Self>(
r#"
UPDATE mock_environments
SET
reality_config = COALESCE($1, reality_config),
chaos_config = COALESCE($2, chaos_config),
drift_budget_config = COALESCE($3, drift_budget_config),
updated_at = NOW()
WHERE id = $4
RETURNING *
"#,
)
.bind(reality_config)
.bind(chaos_config)
.bind(drift_budget_config)
.bind(self.id)
.fetch_one(pool)
.await
}
pub async fn delete(pool: &sqlx::PgPool, id: Uuid) -> sqlx::Result<()> {
sqlx::query("DELETE FROM mock_environments WHERE id = $1")
.bind(id)
.execute(pool)
.await?;
Ok(())
}
pub async fn delete_by_workspace(pool: &sqlx::PgPool, workspace_id: Uuid) -> sqlx::Result<()> {
sqlx::query("DELETE FROM mock_environments WHERE workspace_id = $1")
.bind(workspace_id)
.execute(pool)
.await?;
Ok(())
}
}