backbone-support 0.12.0

Customer support: issues tracked against service level agreements, and warranty claims
Documentation
//! Repository for ServiceLevelPriority 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-target SQL (4-layer rule: services orchestrate, repos 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<ServiceLevelPriority, 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::ServiceLevelPriority;

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

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

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

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

/// The exact row a per-priority SLA target writes.
///
/// Mirrors the raw column shape rather than the `ServiceLevelPriority` entity: `priority` is carried as
/// a free string and cast at the DB (`$3::issue_priority`), which is what lets a bad priority value fail
/// as a DB error instead of a deserialize panic.
pub struct NewSlaPriorityRow<'a> {
    pub id: Uuid,
    pub sla_id: Uuid,
    pub priority: &'a str,
    pub response_time_mins: i32,
    pub resolution_time_mins: i32,
}

/// The deadline-snapshot projection: the matching target's two promised times, in minutes.
pub struct SlaTargetRow {
    pub response_time_mins: i32,
    pub resolution_time_mins: i32,
}

/// Hand-written ServiceLevelPriority SQL. Lives here (not in the write service) per the module's
/// 4-layer rule.
impl ServiceLevelPriorityRepository {
    /// Insert one per-priority target. Takes the CALLER'S connection so it commits with the SLA header
    /// it belongs to. The caller has already relayed the ambient org scope onto that connection —
    /// don't re-bind here.
    pub async fn insert_priority(
        &self,
        conn: &mut sqlx::PgConnection,
        p: &NewSlaPriorityRow<'_>,
    ) -> Result<(), sqlx::Error> {
        sqlx::query(
            r#"INSERT INTO support.service_level_priorities
                 (id, sla_id, priority, response_time_mins, resolution_time_mins)
               VALUES ($1,$2,$3::issue_priority,$4,$5)"#,
        )
        .bind(p.id)
        .bind(p.sla_id)
        .bind(p.priority)
        .bind(p.response_time_mins)
        .bind(p.resolution_time_mins)
        .execute(conn)
        .await?;
        Ok(())
    }

    /// The target an SLA promises for one priority — the source of a raised ticket's snapshotted
    /// deadlines. `Ok(None)` = this SLA makes no promise at that priority.
    pub async fn find_target(
        &self,
        pool: &PgPool,
        sla_id: Uuid,
        priority: &str,
    ) -> Result<Option<SlaTargetRow>, sqlx::Error> {
        let row = org_scope::fetch_optional_row_scoped(
            pool,
            sqlx::query(
                r#"SELECT response_time_mins, resolution_time_mins FROM support.service_level_priorities
                   WHERE sla_id=$1 AND priority=$2::issue_priority"#,
            )
            .bind(sla_id)
            .bind(priority),
        )
        .await?;
        Ok(row.map(|r| SlaTargetRow {
            response_time_mins: r.get("response_time_mins"),
            resolution_time_mins: r.get("resolution_time_mins"),
        }))
    }
}

backbone_core::impl_crud_repository!(
    ServiceLevelPriorityRepository,
    ServiceLevelPriority,
    soft_delete
);