suppression_repo takes a ScriptOwner (app or group), binding the matching owner column. apply_service load_current / create / prune / suppression_report all thread owner.as_script_owner(); the validate_bundle_for group-suppress rejection and the manifest parse guard are dropped — a [group] may now declare [suppress] to decline a template it inherits from a higher ancestor. No runtime consumption effect yet (the chain filters land in M1.3/M1.4). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
109 lines
3.8 KiB
Rust
109 lines
3.8 KiB
Rust
//! Template-suppression markers (§11 tail) — the `template_suppressions` table
|
|
//! (0058, made owner-polymorphic in 0060). A marker `(owner, target_kind,
|
|
//! reference)` records that an OWNER opts OUT of an inherited group template: a
|
|
//! handler script name it declines (`target_kind='trigger'`) or a path it
|
|
//! declines (`target_kind='route'`).
|
|
//!
|
|
//! Owner-polymorphic (like `extension_points`): an **app** declines for itself;
|
|
//! a **group** declines for its whole subtree. The functions take a
|
|
//! `ScriptOwner` and bind the matching owner column. Same tx-function style:
|
|
//! read over `&PgPool`, write over a `&mut Transaction`.
|
|
|
|
use picloud_shared::ScriptOwner;
|
|
use sqlx::{PgPool, Postgres, Transaction};
|
|
|
|
/// The two suppressible template kinds, as stored in `target_kind`.
|
|
pub const TARGET_TRIGGER: &str = "trigger";
|
|
pub const TARGET_ROUTE: &str = "route";
|
|
|
|
/// List the suppression markers declared at `owner`, as `(target_kind,
|
|
/// reference)` pairs, stably ordered. Backs `load_current` (the apply diff) and
|
|
/// the read-only `suppress ls`.
|
|
pub async fn list_for_owner(
|
|
pool: &PgPool,
|
|
owner: ScriptOwner,
|
|
) -> Result<Vec<(String, String)>, sqlx::Error> {
|
|
let sql = match owner {
|
|
ScriptOwner::App(_) => {
|
|
"SELECT target_kind, reference FROM template_suppressions \
|
|
WHERE app_id = $1 ORDER BY target_kind, LOWER(reference)"
|
|
}
|
|
ScriptOwner::Group(_) => {
|
|
"SELECT target_kind, reference FROM template_suppressions \
|
|
WHERE group_id = $1 ORDER BY target_kind, LOWER(reference)"
|
|
}
|
|
};
|
|
sqlx::query_as(sql)
|
|
.bind(owner_uuid(owner))
|
|
.fetch_all(pool)
|
|
.await
|
|
}
|
|
|
|
/// Insert a suppression marker in the apply transaction. Idempotent: re-apply
|
|
/// of an already-declared `(kind, reference)` is a no-op (`ON CONFLICT DO
|
|
/// NOTHING` against the per-owner partial unique index), so the marker survives
|
|
/// without a spurious version bump.
|
|
pub async fn insert_suppression_tx(
|
|
tx: &mut Transaction<'_, Postgres>,
|
|
owner: ScriptOwner,
|
|
target_kind: &str,
|
|
reference: &str,
|
|
) -> Result<(), sqlx::Error> {
|
|
let (app_id, group_id) = owner_columns(owner);
|
|
sqlx::query(
|
|
"INSERT INTO template_suppressions (app_id, group_id, target_kind, reference) \
|
|
VALUES ($1, $2, $3, $4) \
|
|
ON CONFLICT DO NOTHING",
|
|
)
|
|
.bind(app_id)
|
|
.bind(group_id)
|
|
.bind(target_kind)
|
|
.bind(reference)
|
|
.execute(&mut **tx)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Delete a suppression marker in the apply transaction. Used by `--prune` when
|
|
/// the manifest stops declaring the reference → the template re-inherits.
|
|
pub async fn delete_suppression_tx(
|
|
tx: &mut Transaction<'_, Postgres>,
|
|
owner: ScriptOwner,
|
|
target_kind: &str,
|
|
reference: &str,
|
|
) -> Result<(), sqlx::Error> {
|
|
let sql = match owner {
|
|
ScriptOwner::App(_) => {
|
|
"DELETE FROM template_suppressions \
|
|
WHERE app_id = $1 AND target_kind = $2 AND reference = $3"
|
|
}
|
|
ScriptOwner::Group(_) => {
|
|
"DELETE FROM template_suppressions \
|
|
WHERE group_id = $1 AND target_kind = $2 AND reference = $3"
|
|
}
|
|
};
|
|
sqlx::query(sql)
|
|
.bind(owner_uuid(owner))
|
|
.bind(target_kind)
|
|
.bind(reference)
|
|
.execute(&mut **tx)
|
|
.await?;
|
|
Ok(())
|
|
}
|
|
|
|
/// `(app_id, group_id)` bind pair for the polymorphic owner — exactly one set.
|
|
fn owner_columns(owner: ScriptOwner) -> (Option<uuid::Uuid>, Option<uuid::Uuid>) {
|
|
match owner {
|
|
ScriptOwner::App(a) => (Some(a.into_inner()), None),
|
|
ScriptOwner::Group(g) => (None, Some(g.into_inner())),
|
|
}
|
|
}
|
|
|
|
/// The single owner UUID (app or group), for the WHERE-on-one-column queries.
|
|
fn owner_uuid(owner: ScriptOwner) -> uuid::Uuid {
|
|
match owner {
|
|
ScriptOwner::App(a) => a.into_inner(),
|
|
ScriptOwner::Group(g) => g.into_inner(),
|
|
}
|
|
}
|