mockforge-registry-core 0.3.137

Shared domain models, storage abstractions, and OSS-safe handlers for MockForge's registry backends (SaaS Postgres + OSS SQLite admin UI).
Documentation
//! Service model for managing mock API service definitions in cloud mode

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use uuid::Uuid;

#[derive(Debug, Clone, FromRow, Serialize, Deserialize)]
pub struct CloudService {
    pub id: Uuid,
    pub org_id: Uuid,
    pub workspace_id: Option<Uuid>,
    pub name: String,
    pub description: String,
    pub base_url: String,
    pub enabled: bool,
    pub tags: serde_json::Value,
    pub routes: serde_json::Value,
    pub created_by: Uuid,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
}

#[cfg(feature = "postgres")]
impl CloudService {
    #[allow(clippy::too_many_arguments)]
    pub async fn create(
        pool: &sqlx::PgPool,
        org_id: Uuid,
        workspace_id: Option<Uuid>,
        created_by: Uuid,
        name: &str,
        description: &str,
        base_url: &str,
    ) -> sqlx::Result<Self> {
        sqlx::query_as::<_, Self>(
            r#"
            INSERT INTO services (org_id, workspace_id, name, description, base_url, created_by)
            VALUES ($1, $2, $3, $4, $5, $6)
            RETURNING *
            "#,
        )
        .bind(org_id)
        .bind(workspace_id)
        .bind(name)
        .bind(description)
        .bind(base_url)
        .bind(created_by)
        .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 services WHERE id = $1")
            .bind(id)
            .fetch_optional(pool)
            .await
    }

    pub async fn find_by_org(pool: &sqlx::PgPool, org_id: Uuid) -> sqlx::Result<Vec<Self>> {
        sqlx::query_as::<_, Self>(
            "SELECT * FROM services WHERE org_id = $1 ORDER BY created_at DESC",
        )
        .bind(org_id)
        .fetch_all(pool)
        .await
    }

    pub async fn find_by_workspace(
        pool: &sqlx::PgPool,
        org_id: Uuid,
        workspace_id: Uuid,
    ) -> sqlx::Result<Vec<Self>> {
        sqlx::query_as::<_, Self>(
            "SELECT * FROM services \
             WHERE org_id = $1 AND workspace_id = $2 \
             ORDER BY created_at DESC",
        )
        .bind(org_id)
        .bind(workspace_id)
        .fetch_all(pool)
        .await
    }

    /// Update a service. The `workspace_id` argument is tri-state so callers
    /// can distinguish "don't change this column" from "explicitly clear it":
    /// `None` leaves the column alone, `Some(None)` writes SQL NULL
    /// (unassigns), and `Some(Some(id))` assigns to workspace `id`.
    #[allow(clippy::too_many_arguments)]
    pub async fn update(
        pool: &sqlx::PgPool,
        id: Uuid,
        name: Option<&str>,
        description: Option<&str>,
        base_url: Option<&str>,
        enabled: Option<bool>,
        tags: Option<&serde_json::Value>,
        routes: Option<&serde_json::Value>,
        workspace_id: Option<Option<Uuid>>,
    ) -> sqlx::Result<Option<Self>> {
        let update_workspace = workspace_id.is_some();
        let workspace_value = workspace_id.flatten();
        sqlx::query_as::<_, Self>(
            r#"
            UPDATE services
            SET
                name = COALESCE($2, name),
                description = COALESCE($3, description),
                base_url = COALESCE($4, base_url),
                enabled = COALESCE($5, enabled),
                tags = COALESCE($6, tags),
                routes = COALESCE($7, routes),
                workspace_id = CASE WHEN $9 THEN $8 ELSE workspace_id END,
                updated_at = NOW()
            WHERE id = $1
            RETURNING *
            "#,
        )
        .bind(id)
        .bind(name)
        .bind(description)
        .bind(base_url)
        .bind(enabled)
        .bind(tags)
        .bind(routes)
        .bind(workspace_value)
        .bind(update_workspace)
        .fetch_optional(pool)
        .await
    }

    pub async fn delete(pool: &sqlx::PgPool, id: Uuid) -> sqlx::Result<()> {
        sqlx::query("DELETE FROM services WHERE id = $1").bind(id).execute(pool).await?;
        Ok(())
    }
}