use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use uuid::Uuid;
#[derive(Debug, Clone, FromRow, Serialize, Deserialize)]
pub struct OrgTemplate {
pub id: Uuid,
pub org_id: Uuid,
pub name: String,
pub description: Option<String>,
pub blueprint_config: serde_json::Value,
pub security_baseline: serde_json::Value,
pub created_by: Uuid,
pub is_default: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[cfg(feature = "postgres")]
impl OrgTemplate {
#[allow(clippy::too_many_arguments)]
pub async fn create(
pool: &sqlx::PgPool,
org_id: Uuid,
name: &str,
description: Option<&str>,
blueprint_config: Option<serde_json::Value>,
security_baseline: Option<serde_json::Value>,
created_by: Uuid,
is_default: bool,
) -> sqlx::Result<Self> {
if is_default {
sqlx::query("UPDATE org_templates SET is_default = FALSE WHERE org_id = $1")
.bind(org_id)
.execute(pool)
.await?;
}
sqlx::query_as::<_, Self>(
r#"
INSERT INTO org_templates (
org_id, name, description, blueprint_config, security_baseline,
created_by, is_default
)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *
"#,
)
.bind(org_id)
.bind(name)
.bind(description)
.bind(blueprint_config.unwrap_or_else(|| serde_json::json!({})))
.bind(security_baseline.unwrap_or_else(|| serde_json::json!({})))
.bind(created_by)
.bind(is_default)
.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 org_templates WHERE id = $1")
.bind(id)
.fetch_optional(pool)
.await
}
pub async fn list_by_org(pool: &sqlx::PgPool, org_id: Uuid) -> sqlx::Result<Vec<Self>> {
sqlx::query_as::<_, Self>(
"SELECT * FROM org_templates WHERE org_id = $1 ORDER BY is_default DESC, name",
)
.bind(org_id)
.fetch_all(pool)
.await
}
pub async fn get_default(pool: &sqlx::PgPool, org_id: Uuid) -> sqlx::Result<Option<Self>> {
sqlx::query_as::<_, Self>(
"SELECT * FROM org_templates WHERE org_id = $1 AND is_default = TRUE LIMIT 1",
)
.bind(org_id)
.fetch_optional(pool)
.await
}
pub async fn update(
&self,
pool: &sqlx::PgPool,
name: Option<&str>,
description: Option<&str>,
blueprint_config: Option<serde_json::Value>,
security_baseline: Option<serde_json::Value>,
is_default: Option<bool>,
) -> sqlx::Result<Self> {
if is_default == Some(true) {
sqlx::query(
"UPDATE org_templates SET is_default = FALSE WHERE org_id = $1 AND id != $2",
)
.bind(self.org_id)
.bind(self.id)
.execute(pool)
.await?;
}
sqlx::query_as::<_, Self>(
r#"
UPDATE org_templates
SET
name = COALESCE($1, name),
description = COALESCE($2, description),
blueprint_config = COALESCE($3, blueprint_config),
security_baseline = COALESCE($4, security_baseline),
is_default = COALESCE($5, is_default),
updated_at = NOW()
WHERE id = $6
RETURNING *
"#,
)
.bind(name)
.bind(description)
.bind(blueprint_config)
.bind(security_baseline)
.bind(is_default)
.bind(self.id)
.fetch_one(pool)
.await
}
pub async fn delete(pool: &sqlx::PgPool, id: Uuid) -> sqlx::Result<()> {
sqlx::query("DELETE FROM org_templates WHERE id = $1")
.bind(id)
.execute(pool)
.await?;
Ok(())
}
pub async fn set_as_default(pool: &sqlx::PgPool, id: Uuid, org_id: Uuid) -> sqlx::Result<Self> {
sqlx::query("UPDATE org_templates SET is_default = FALSE WHERE org_id = $1")
.bind(org_id)
.execute(pool)
.await?;
sqlx::query_as::<_, Self>(
"UPDATE org_templates SET is_default = TRUE, updated_at = NOW() WHERE id = $1 RETURNING *",
)
.bind(id)
.fetch_one(pool)
.await
}
}