use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use uuid::Uuid;
#[derive(Debug, Clone, FromRow, Serialize, Deserialize)]
pub struct CloudFixture {
pub id: Uuid,
pub org_id: Uuid,
pub workspace_id: Option<Uuid>,
pub name: String,
pub description: String,
pub path: String,
pub method: String,
pub content: Option<serde_json::Value>,
pub tags: serde_json::Value,
pub route_path: Option<String>,
pub protocol: Option<String>,
pub created_by: Uuid,
#[sqlx(default)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created_by_username: Option<String>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[cfg(feature = "postgres")]
impl CloudFixture {
#[allow(clippy::too_many_arguments)]
pub async fn create(
pool: &sqlx::PgPool,
org_id: Uuid,
created_by: Uuid,
name: &str,
description: &str,
path: &str,
method: &str,
content: Option<&serde_json::Value>,
protocol: Option<&str>,
tags: Option<&serde_json::Value>,
workspace_id: Option<Uuid>,
route_path: Option<&str>,
) -> sqlx::Result<Self> {
let tags_value = tags.cloned().unwrap_or_else(|| serde_json::Value::Array(Vec::new()));
sqlx::query_as::<_, Self>(
r#"
WITH inserted AS (
INSERT INTO fixtures (
org_id, workspace_id, name, description, path, method,
content, protocol, tags, route_path, created_by
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
RETURNING *
)
SELECT i.*, u.username AS created_by_username
FROM inserted i
LEFT JOIN users u ON u.id = i.created_by
"#,
)
.bind(org_id)
.bind(workspace_id)
.bind(name)
.bind(description)
.bind(path)
.bind(method)
.bind(content)
.bind(protocol)
.bind(tags_value)
.bind(route_path)
.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>(
r#"
SELECT f.*, u.username AS created_by_username
FROM fixtures f
LEFT JOIN users u ON u.id = f.created_by
WHERE f.id = $1
"#,
)
.bind(id)
.fetch_optional(pool)
.await
}
pub async fn find_by_org(
pool: &sqlx::PgPool,
org_id: Uuid,
workspace_id: Option<Uuid>,
) -> sqlx::Result<Vec<Self>> {
sqlx::query_as::<_, Self>(
r#"
SELECT f.*, u.username AS created_by_username
FROM fixtures f
LEFT JOIN users u ON u.id = f.created_by
WHERE f.org_id = $1
AND ($2::uuid IS NULL OR f.workspace_id = $2)
ORDER BY f.created_at DESC
"#,
)
.bind(org_id)
.bind(workspace_id)
.fetch_all(pool)
.await
}
#[allow(clippy::too_many_arguments)]
pub async fn update(
pool: &sqlx::PgPool,
id: Uuid,
name: Option<&str>,
description: Option<&str>,
path: Option<&str>,
method: Option<&str>,
content: Option<&serde_json::Value>,
protocol: Option<&str>,
tags: Option<&serde_json::Value>,
route_path: Option<&str>,
workspace_id: Option<Option<Uuid>>,
) -> sqlx::Result<Option<Self>> {
let (workspace_set, workspace_value) = match workspace_id {
Some(value) => (true, value),
None => (false, None),
};
sqlx::query_as::<_, Self>(
r#"
WITH updated AS (
UPDATE fixtures
SET
name = COALESCE($2, name),
description = COALESCE($3, description),
path = COALESCE($4, path),
method = COALESCE($5, method),
content = COALESCE($6, content),
protocol = COALESCE($7, protocol),
tags = COALESCE($8, tags),
route_path = COALESCE($9, route_path),
workspace_id = CASE WHEN $10 THEN $11 ELSE workspace_id END,
updated_at = NOW()
WHERE id = $1
RETURNING *
)
SELECT u_row.*, u.username AS created_by_username
FROM updated u_row
LEFT JOIN users u ON u.id = u_row.created_by
"#,
)
.bind(id)
.bind(name)
.bind(description)
.bind(path)
.bind(method)
.bind(content)
.bind(protocol)
.bind(tags)
.bind(route_path)
.bind(workspace_set)
.bind(workspace_value)
.fetch_optional(pool)
.await
}
pub async fn delete(pool: &sqlx::PgPool, id: Uuid) -> sqlx::Result<()> {
sqlx::query("DELETE FROM fixtures WHERE id = $1").bind(id).execute(pool).await?;
Ok(())
}
pub async fn delete_many(
pool: &sqlx::PgPool,
org_id: Uuid,
ids: &[Uuid],
) -> sqlx::Result<Vec<Uuid>> {
if ids.is_empty() {
return Ok(Vec::new());
}
let rows: Vec<(Uuid,)> = sqlx::query_as(
r#"
DELETE FROM fixtures
WHERE id = ANY($1) AND org_id = $2
RETURNING id
"#,
)
.bind(ids)
.bind(org_id)
.fetch_all(pool)
.await?;
Ok(rows.into_iter().map(|(id,)| id).collect())
}
}