backbone-support 0.12.0

Customer support: issues tracked against service level agreements, and warranty claims
Documentation
//! Repository for ServiceLevelAgreement entities
//!
//! Originally generated by metaphor-schema; now **user-owned** — this exact path is declared under
//! `user_owned` in `metaphor.codegen.yaml`, so the generator skips it wholesale. The custom methods
//! below hold the hand-written SLA SQL (4-layer rule: services orchestrate, repositories hold the SQL).
//!
//! Tenancy (ADR-0029): the SQL here carries no tenant key. The scoped-execute helpers ride the
//! request-dedicated connection when the composing service bound one (its fence variables govern
//! what the RLS layer accepts) and fall back to a plain pool execute otherwise.
//!
//! Thin newtype over `backbone_orm::GenericCrudRepository<ServiceLevelAgreement, backbone_orm::SoftDelete>`.
//! All standard CRUD methods are available via `Deref`.

use anyhow::Result;
use sqlx::{PgPool, Row};
use uuid::Uuid;

use backbone_orm::org_scope;

use crate::domain::entity::ServiceLevelAgreement;

/// Table name for ServiceLevelAgreement entities
pub const TABLE_NAME: &str = "support.service_level_agreements";

/// Repository for ServiceLevelAgreement entities.
///
/// All standard CRUD, soft-delete, pagination, and bulk methods are
/// provided automatically via `Deref` to `backbone_orm::GenericCrudRepository`.
pub struct ServiceLevelAgreementRepository(
    backbone_orm::GenericCrudRepository<ServiceLevelAgreement, backbone_orm::SoftDelete>,
);

impl std::ops::Deref for ServiceLevelAgreementRepository {
    type Target =
        backbone_orm::GenericCrudRepository<ServiceLevelAgreement, backbone_orm::SoftDelete>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl ServiceLevelAgreementRepository {
    /// Create a new repository instance.
    pub fn new(pool: PgPool) -> Self {
        Self(backbone_orm::GenericCrudRepository::new(pool, TABLE_NAME))
    }
}

/// The exact row an SLA definition writes.
///
/// Mirrors the raw column shape rather than the `ServiceLevelAgreement` entity — the write service
/// composes it from its `NewSla` DTO and never hands a generated entity to the SQL.
pub struct NewSlaRow<'a> {
    pub id: Uuid,
    pub name: &'a str,
    pub is_default: bool,
}

/// Hand-written ServiceLevelAgreement SQL. Lives here (not in the write service) per the module's
/// 4-layer rule: services orchestrate and own the unit of work, repositories hold the SQL.
impl ServiceLevelAgreementRepository {
    /// Insert the SLA header. Takes the CALLER'S connection so the header and its per-priority target
    /// rows commit as one unit. The caller has already relayed the ambient org scope onto that
    /// connection — don't re-bind here.
    pub async fn insert_sla(
        &self,
        conn: &mut sqlx::PgConnection,
        s: &NewSlaRow<'_>,
    ) -> Result<(), sqlx::Error> {
        sqlx::query(
            r#"INSERT INTO support.service_level_agreements (id, name, is_default, status)
               VALUES ($1,$2,$3,'active')"#,
        )
        .bind(s.id)
        .bind(s.name)
        .bind(s.is_default)
        .execute(conn)
        .await?;
        Ok(())
    }

    /// The default active SLA, if one exists — the fallback when a ticket is raised without an
    /// explicit SLA. Under a composing service's row fence (RLS), the scope bound on the request
    /// connection limits what is visible; with no fence mounted, this is the deployment's single
    /// default.
    pub async fn find_default_id(&self, pool: &PgPool) -> Result<Option<Uuid>, sqlx::Error> {
        let row = org_scope::fetch_optional_row_scoped(
            pool,
            sqlx::query(
                r#"SELECT id FROM support.service_level_agreements
                   WHERE is_default=true AND status='active'
                     AND (metadata->>'deleted_at') IS NULL LIMIT 1"#,
            ),
        )
        .await?;
        Ok(row.map(|r| r.get::<Uuid, _>("id")))
    }
}

backbone_core::impl_crud_repository!(
    ServiceLevelAgreementRepository,
    ServiceLevelAgreement,
    soft_delete
);