Files
member-console/internal/entitlements/entitlement_set_changes.sql.go
T
cgalo5758 f8a3478f2a Rebuild the entitlement set Rules surface as a staged batch
The Rules section is one record table grouped by kind, Limit then
Boolean, on fixed columns, edited in place: Edit opens a row's controls
in their columns, Add rule opens a dense row above the table, and every
change is staged into a tray that lists the deltas with Undo and applies
them as one rule-change act. The reduction policy is a column of the
rule beside its limit. History shows counts only. Group rows are a quiet
heading rather than a divider, the maintainer's pick from four rounds of
outside-model ideation.

Dense rows align to the top and render each error under its control in
every form family (design D16), replacing the below-row error block; the
forms library gains the batch form (rows plus one tray) and the RowField
dense and label-hidden options. Migration 00019 records the governing
reduction policy on effect rows.

Archive staged-rule-changes with its spec updates (entitlement-set-
management, entitlement-set-history, entitlements, form-library,
form-conventions, ui-quality-gate). Screens accepted 2026-09-19.
2026-09-19 19:46:09 -05:00

780 lines
27 KiB
Go

// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial
// SPDX-FileCopyrightText: 2025-2026 Christian Galo
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: entitlement_set_changes.sql
package entitlements
import (
"context"
"database/sql"
"encoding/json"
"time"
"github.com/google/uuid"
"github.com/lib/pq"
"github.com/sqlc-dev/pqtype"
)
const commitRuleChange = `-- name: CommitRuleChange :one
SELECT (r).change_id::uuid AS change_id,
(r).rule_id::uuid AS rule_id,
(r).sync_path::varchar AS sync_path,
(r).pool_count::integer AS pool_count,
(r).org_count::integer AS org_count,
(r).suspended_only_count::integer AS suspended_only_count
FROM core.commit_rule_change(
$1::uuid,
$2::varchar,
$3::uuid,
$4::jsonb,
$5::varchar,
$6::uuid,
$7::uuid,
$8::text,
$9::varchar,
$10::timestamptz,
$11::integer
) AS r
`
type CommitRuleChangeParams struct {
SetID string `json:"set_id"`
ChangeKind string `json:"change_kind"`
RuleID uuid.NullUUID `json:"rule_id"`
Rule json.RawMessage `json:"rule"`
ActorType string `json:"actor_type"`
ActorPersonID uuid.NullUUID `json:"actor_person_id"`
ActorServiceAccountID uuid.NullUUID `json:"actor_service_account_id"`
Note sql.NullString `json:"note"`
RequestID sql.NullString `json:"request_id"`
EffectiveAt time.Time `json:"effective_at"`
SyncCap int32 `json:"sync_cap"`
}
type CommitRuleChangeRow struct {
ChangeID string `json:"change_id"`
RuleID string `json:"rule_id"`
SyncPath string `json:"sync_path"`
PoolCount int32 `json:"pool_count"`
OrgCount int32 `json:"org_count"`
SuspendedOnlyCount int32 `json:"suspended_only_count"`
}
// The act rows (core.entitlement_set_changes) and the effect rows
// (core.entitlement_set_change_effects) are append-only: nothing in this file
// updates or deletes one. Both are written by core.commit_rule_change and
// core.settle_obligation, so no Create query exists for any of the three
// tables. The obligations are the one mutable table, a work list rather than
// history, and only its status columns move.
// The only write path to core.entitlement_set_rules. The calling transaction
// must hold the exclusive materialization rendezvous, which the function
// asserts: open it with BeginRuleChange.
func (q *Queries) CommitRuleChange(ctx context.Context, arg CommitRuleChangeParams) (CommitRuleChangeRow, error) {
row := q.db.QueryRowContext(ctx, commitRuleChange,
arg.SetID,
arg.ChangeKind,
arg.RuleID,
arg.Rule,
arg.ActorType,
arg.ActorPersonID,
arg.ActorServiceAccountID,
arg.Note,
arg.RequestID,
arg.EffectiveAt,
arg.SyncCap,
)
var i CommitRuleChangeRow
err := row.Scan(
&i.ChangeID,
&i.RuleID,
&i.SyncPath,
&i.PoolCount,
&i.OrgCount,
&i.SuspendedOnlyCount,
)
return i, err
}
const countEntitlementSetChangeEffectsByOrg = `-- name: CountEntitlementSetChangeEffectsByOrg :one
SELECT COUNT(*)::BIGINT AS effect_count
FROM core.entitlement_set_change_effects
WHERE org_id = $1
`
func (q *Queries) CountEntitlementSetChangeEffectsByOrg(ctx context.Context, orgID string) (int64, error) {
row := q.db.QueryRowContext(ctx, countEntitlementSetChangeEffectsByOrg, orgID)
var effect_count int64
err := row.Scan(&effect_count)
return effect_count, err
}
const countEntitlementSetChanges = `-- name: CountEntitlementSetChanges :one
SELECT COUNT(*)::BIGINT AS change_count
FROM core.entitlement_set_changes
WHERE set_id = $1
`
func (q *Queries) CountEntitlementSetChanges(ctx context.Context, setID string) (int64, error) {
row := q.db.QueryRowContext(ctx, countEntitlementSetChanges, setID)
var change_count int64
err := row.Scan(&change_count)
return change_count, err
}
const countFailedObligationsBySets = `-- name: CountFailedObligationsBySets :many
SELECT c.set_id, COUNT(*)::BIGINT AS failed_count
FROM core.entitlement_set_change_obligations o
JOIN core.entitlement_set_changes c ON c.change_id = o.change_id
WHERE c.set_id = ANY($1::uuid[]) AND o.status = 'failed'
GROUP BY c.set_id
ORDER BY c.set_id ASC
`
type CountFailedObligationsBySetsRow struct {
SetID string `json:"set_id"`
FailedCount int64 `json:"failed_count"`
}
// The same count for a page of sets in one query rather than one per row.
func (q *Queries) CountFailedObligationsBySets(ctx context.Context, setIds []string) ([]CountFailedObligationsBySetsRow, error) {
rows, err := q.db.QueryContext(ctx, countFailedObligationsBySets, pq.Array(setIds))
if err != nil {
return nil, err
}
defer rows.Close()
items := []CountFailedObligationsBySetsRow{}
for rows.Next() {
var i CountFailedObligationsBySetsRow
if err := rows.Scan(&i.SetID, &i.FailedCount); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const countUnsettledObligationsBySet = `-- name: CountUnsettledObligationsBySet :one
SELECT
COUNT(*) FILTER (WHERE o.status = 'pending')::BIGINT AS pending_count,
COUNT(*) FILTER (WHERE o.status = 'failed')::BIGINT AS failed_count
FROM core.entitlement_set_change_obligations o
JOIN core.entitlement_set_changes c ON c.change_id = o.change_id
WHERE c.set_id = $1
`
type CountUnsettledObligationsBySetRow struct {
PendingCount int64 `json:"pending_count"`
FailedCount int64 `json:"failed_count"`
}
// What the set's Rules section reads: still draining and failed, separately.
func (q *Queries) CountUnsettledObligationsBySet(ctx context.Context, setID string) (CountUnsettledObligationsBySetRow, error) {
row := q.db.QueryRowContext(ctx, countUnsettledObligationsBySet, setID)
var i CountUnsettledObligationsBySetRow
err := row.Scan(&i.PendingCount, &i.FailedCount)
return i, err
}
const getObligationStatus = `-- name: GetObligationStatus :one
SELECT status, attempts
FROM core.entitlement_set_change_obligations
WHERE change_id = $1 AND pool_id = $2
`
type GetObligationStatusParams struct {
ChangeID string `json:"change_id"`
PoolID string `json:"pool_id"`
}
type GetObligationStatusRow struct {
Status string `json:"status"`
Attempts int32 `json:"attempts"`
}
// One obligation's state, which the drain reads before it does any work: an
// obligation already settled is returned to a redispatched drain untouched.
func (q *Queries) GetObligationStatus(ctx context.Context, arg GetObligationStatusParams) (GetObligationStatusRow, error) {
row := q.db.QueryRowContext(ctx, getObligationStatus, arg.ChangeID, arg.PoolID)
var i GetObligationStatusRow
err := row.Scan(&i.Status, &i.Attempts)
return i, err
}
const getRuleChangeStamp = `-- name: GetRuleChangeStamp :one
SELECT r.resource_key, r.tier_reduction_policy
FROM core.entitlement_set_changes c
JOIN core.entitlement_set_rules r ON r.rule_id = c.rule_id
WHERE c.change_id = $1
`
type GetRuleChangeStampRow struct {
ResourceKey sql.NullString `json:"resource_key"`
TierReductionPolicy string `json:"tier_reduction_policy"`
}
// The changed rule's resource key and reduction policy. The policy is
// stamped on that key's effect row alone (doc-47 §5.2: the reduction policy
// is the changed rule's, where the key is the rule's), so the key comes back
// with it. The drain reads the pair once per change.
func (q *Queries) GetRuleChangeStamp(ctx context.Context, changeID string) (GetRuleChangeStampRow, error) {
row := q.db.QueryRowContext(ctx, getRuleChangeStamp, changeID)
var i GetRuleChangeStampRow
err := row.Scan(&i.ResourceKey, &i.TierReductionPolicy)
return i, err
}
const listChangesSharingRequest = `-- name: ListChangesSharingRequest :many
SELECT c.change_id, r.resource_key, r.tier_reduction_policy
FROM core.entitlement_set_changes c
JOIN core.entitlement_set_rules r ON r.rule_id = c.rule_id
JOIN core.entitlement_set_changes named ON named.change_id = $1::uuid
WHERE c.set_id = named.set_id
AND (c.change_id = named.change_id
OR (named.request_id IS NOT NULL AND c.request_id = named.request_id))
ORDER BY r.resource_key COLLATE "C" ASC NULLS FIRST, c.change_id ASC
`
type ListChangesSharingRequestRow struct {
ChangeID string `json:"change_id"`
ResourceKey sql.NullString `json:"resource_key"`
TierReductionPolicy string `json:"tier_reduction_policy"`
}
// The acts of one applied batch: the change named and every other act on the
// same set carrying the same request identifier, which is what relates them
// (entitlement-set-history, "Every committed act writes one change row and
// one effect row per pool and key that moved"). Each comes back with its
// rule's key and reduction policy, so the drain stamps and routes from one
// read. The order is the commit's own, resource key ascending under the C
// collation, which is the byte order Go sorted the deltas by, so the drain
// re-derives the order the acts were filed in whatever the database's own
// collation is; the first row is then the batch's first act, the one a
// movement on a key no act names hangs off. A change with no request
// identifier returns itself alone.
func (q *Queries) ListChangesSharingRequest(ctx context.Context, changeID string) ([]ListChangesSharingRequestRow, error) {
rows, err := q.db.QueryContext(ctx, listChangesSharingRequest, changeID)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListChangesSharingRequestRow{}
for rows.Next() {
var i ListChangesSharingRequestRow
if err := rows.Scan(&i.ChangeID, &i.ResourceKey, &i.TierReductionPolicy); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listChangesWithUnsettledObligations = `-- name: ListChangesWithUnsettledObligations :many
SELECT DISTINCT o.change_id, c.set_id, MIN(o.created_at)::timestamptz AS oldest_created_at
FROM core.entitlement_set_change_obligations o
JOIN core.entitlement_set_changes c ON c.change_id = o.change_id
WHERE o.status = 'pending'
GROUP BY o.change_id, c.set_id
ORDER BY oldest_created_at ASC, o.change_id ASC
LIMIT $1
`
type ListChangesWithUnsettledObligationsRow struct {
ChangeID string `json:"change_id"`
SetID string `json:"set_id"`
OldestCreatedAt time.Time `json:"oldest_created_at"`
}
// The poller's entry point, oldest act first: changes that still owe a
// pending pool. A change whose remaining rows are all failed owes the drain
// nothing until an operator's Retry requeues them.
func (q *Queries) ListChangesWithUnsettledObligations(ctx context.Context, limit int32) ([]ListChangesWithUnsettledObligationsRow, error) {
rows, err := q.db.QueryContext(ctx, listChangesWithUnsettledObligations, limit)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListChangesWithUnsettledObligationsRow{}
for rows.Next() {
var i ListChangesWithUnsettledObligationsRow
if err := rows.Scan(&i.ChangeID, &i.SetID, &i.OldestCreatedAt); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listEntitlementSetChangeEffects = `-- name: ListEntitlementSetChangeEffects :many
SELECT e.effect_id, e.change_id, e.pool_id, e.org_id, e.org_name,
e.resource_key, e.resource_label,
e.limit_before, e.limit_after, e.granted_before, e.granted_after,
e.usage_at_effect, e.over_usage, e.was_over_before, e.reduction_policy, e.created_at,
e.governing_policy
FROM core.entitlement_set_change_effects e
WHERE e.change_id = $1
ORDER BY e.org_name ASC, e.resource_key ASC
LIMIT $2
`
type ListEntitlementSetChangeEffectsParams struct {
ChangeID string `json:"change_id"`
Limit int32 `json:"limit"`
}
// The History row's expander: what one change did, per organization and key.
func (q *Queries) ListEntitlementSetChangeEffects(ctx context.Context, arg ListEntitlementSetChangeEffectsParams) ([]EntitlementSetChangeEffect, error) {
rows, err := q.db.QueryContext(ctx, listEntitlementSetChangeEffects, arg.ChangeID, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
items := []EntitlementSetChangeEffect{}
for rows.Next() {
var i EntitlementSetChangeEffect
if err := rows.Scan(
&i.EffectID,
&i.ChangeID,
&i.PoolID,
&i.OrgID,
&i.OrgName,
&i.ResourceKey,
&i.ResourceLabel,
&i.LimitBefore,
&i.LimitAfter,
&i.GrantedBefore,
&i.GrantedAfter,
&i.UsageAtEffect,
&i.OverUsage,
&i.WasOverBefore,
&i.ReductionPolicy,
&i.CreatedAt,
&i.GoverningPolicy,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listEntitlementSetChangeEffectsByOrg = `-- name: ListEntitlementSetChangeEffectsByOrg :many
SELECT e.effect_id, e.change_id, e.pool_id, e.org_id, e.org_name,
e.resource_key, e.resource_label,
e.limit_before, e.limit_after, e.granted_before, e.granted_after,
e.usage_at_effect, e.over_usage, e.was_over_before,
e.reduction_policy, e.governing_policy, e.created_at,
c.set_id, c.set_name, c.change_kind, c.effective_at,
c.note, c.actor_type, c.actor_person_id, c.actor_service_account_id
FROM core.entitlement_set_change_effects e
JOIN core.entitlement_set_changes c ON c.change_id = e.change_id
WHERE e.org_id = $1
ORDER BY e.created_at DESC, e.effect_id DESC
LIMIT $2 OFFSET $3
`
type ListEntitlementSetChangeEffectsByOrgParams struct {
OrgID string `json:"org_id"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
type ListEntitlementSetChangeEffectsByOrgRow struct {
EffectID string `json:"effect_id"`
ChangeID string `json:"change_id"`
PoolID string `json:"pool_id"`
OrgID string `json:"org_id"`
OrgName string `json:"org_name"`
ResourceKey string `json:"resource_key"`
ResourceLabel string `json:"resource_label"`
LimitBefore sql.NullInt64 `json:"limit_before"`
LimitAfter sql.NullInt64 `json:"limit_after"`
GrantedBefore sql.NullBool `json:"granted_before"`
GrantedAfter sql.NullBool `json:"granted_after"`
UsageAtEffect sql.NullInt64 `json:"usage_at_effect"`
OverUsage bool `json:"over_usage"`
WasOverBefore bool `json:"was_over_before"`
ReductionPolicy sql.NullString `json:"reduction_policy"`
GoverningPolicy sql.NullString `json:"governing_policy"`
CreatedAt time.Time `json:"created_at"`
SetID string `json:"set_id"`
SetName string `json:"set_name"`
ChangeKind string `json:"change_kind"`
EffectiveAt time.Time `json:"effective_at"`
Note sql.NullString `json:"note"`
ActorType string `json:"actor_type"`
ActorPersonID uuid.NullUUID `json:"actor_person_id"`
ActorServiceAccountID uuid.NullUUID `json:"actor_service_account_id"`
}
// The organization's trail: every rule change that moved one of its pools.
// This is the effect row's audit projection (entitlement-set-history, "The
// change row and every effect row project onto the audit view shape"): the
// pool as the resource, the act's actor triple and occurrence reached through
// the foreign key, and the payload's fields, which carry both policies, the
// rule's own and the one that governed the pool and key.
func (q *Queries) ListEntitlementSetChangeEffectsByOrg(ctx context.Context, arg ListEntitlementSetChangeEffectsByOrgParams) ([]ListEntitlementSetChangeEffectsByOrgRow, error) {
rows, err := q.db.QueryContext(ctx, listEntitlementSetChangeEffectsByOrg, arg.OrgID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListEntitlementSetChangeEffectsByOrgRow{}
for rows.Next() {
var i ListEntitlementSetChangeEffectsByOrgRow
if err := rows.Scan(
&i.EffectID,
&i.ChangeID,
&i.PoolID,
&i.OrgID,
&i.OrgName,
&i.ResourceKey,
&i.ResourceLabel,
&i.LimitBefore,
&i.LimitAfter,
&i.GrantedBefore,
&i.GrantedAfter,
&i.UsageAtEffect,
&i.OverUsage,
&i.WasOverBefore,
&i.ReductionPolicy,
&i.GoverningPolicy,
&i.CreatedAt,
&i.SetID,
&i.SetName,
&i.ChangeKind,
&i.EffectiveAt,
&i.Note,
&i.ActorType,
&i.ActorPersonID,
&i.ActorServiceAccountID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listEntitlementSetChanges = `-- name: ListEntitlementSetChanges :many
SELECT c.change_id, c.set_id, c.set_name, c.change_kind, c.rule_id,
c.resource_key, c.resource_label, c.rule_before, c.rule_after, c.rule_type,
c.value_before, c.value_after, c.per_unit_before, c.per_unit_after,
c.actor_type, c.actor_person_id, c.actor_service_account_id,
c.note, c.request_id, c.sync_path, c.effective_at, c.created_at,
(SELECT COUNT(*) FROM core.entitlement_set_change_obligations o
WHERE o.change_id = c.change_id)::BIGINT AS pools_enumerated,
(SELECT COUNT(*) FROM core.entitlement_set_change_obligations o
WHERE o.change_id = c.change_id AND o.status = 'settled')::BIGINT AS pools_settled,
(SELECT COUNT(*) FROM core.entitlement_set_change_obligations o
WHERE o.change_id = c.change_id AND o.status = 'failed')::BIGINT AS pools_failed,
(SELECT COUNT(DISTINCT e.pool_id) FROM core.entitlement_set_change_effects e
WHERE e.change_id = c.change_id)::BIGINT AS pools_with_effects,
(SELECT COUNT(DISTINCT e.pool_id) FROM core.entitlement_set_change_effects e
WHERE e.change_id = c.change_id AND e.over_usage)::BIGINT AS pools_over_usage
FROM core.entitlement_set_changes c
WHERE c.set_id = $1
ORDER BY c.effective_at DESC, c.created_at DESC, c.change_id DESC
LIMIT $2 OFFSET $3
`
type ListEntitlementSetChangesParams struct {
SetID string `json:"set_id"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
type ListEntitlementSetChangesRow struct {
ChangeID string `json:"change_id"`
SetID string `json:"set_id"`
SetName string `json:"set_name"`
ChangeKind string `json:"change_kind"`
RuleID string `json:"rule_id"`
ResourceKey sql.NullString `json:"resource_key"`
ResourceLabel sql.NullString `json:"resource_label"`
RuleBefore pqtype.NullRawMessage `json:"rule_before"`
RuleAfter json.RawMessage `json:"rule_after"`
RuleType sql.NullString `json:"rule_type"`
ValueBefore sql.NullInt64 `json:"value_before"`
ValueAfter sql.NullInt64 `json:"value_after"`
PerUnitBefore sql.NullBool `json:"per_unit_before"`
PerUnitAfter sql.NullBool `json:"per_unit_after"`
ActorType string `json:"actor_type"`
ActorPersonID uuid.NullUUID `json:"actor_person_id"`
ActorServiceAccountID uuid.NullUUID `json:"actor_service_account_id"`
Note sql.NullString `json:"note"`
RequestID sql.NullString `json:"request_id"`
SyncPath string `json:"sync_path"`
EffectiveAt time.Time `json:"effective_at"`
CreatedAt time.Time `json:"created_at"`
PoolsEnumerated int64 `json:"pools_enumerated"`
PoolsSettled int64 `json:"pools_settled"`
PoolsFailed int64 `json:"pools_failed"`
PoolsWithEffects int64 `json:"pools_with_effects"`
PoolsOverUsage int64 `json:"pools_over_usage"`
}
// The paged History of one set. The per-change counts are correlated
// subqueries over the obligations and the effects, not stored columns.
func (q *Queries) ListEntitlementSetChanges(ctx context.Context, arg ListEntitlementSetChangesParams) ([]ListEntitlementSetChangesRow, error) {
rows, err := q.db.QueryContext(ctx, listEntitlementSetChanges, arg.SetID, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListEntitlementSetChangesRow{}
for rows.Next() {
var i ListEntitlementSetChangesRow
if err := rows.Scan(
&i.ChangeID,
&i.SetID,
&i.SetName,
&i.ChangeKind,
&i.RuleID,
&i.ResourceKey,
&i.ResourceLabel,
&i.RuleBefore,
&i.RuleAfter,
&i.RuleType,
&i.ValueBefore,
&i.ValueAfter,
&i.PerUnitBefore,
&i.PerUnitAfter,
&i.ActorType,
&i.ActorPersonID,
&i.ActorServiceAccountID,
&i.Note,
&i.RequestID,
&i.SyncPath,
&i.EffectiveAt,
&i.CreatedAt,
&i.PoolsEnumerated,
&i.PoolsSettled,
&i.PoolsFailed,
&i.PoolsWithEffects,
&i.PoolsOverUsage,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listFailedObligations = `-- name: ListFailedObligations :many
SELECT o.change_id, o.pool_id, o.attempts, o.last_attempt_at, o.last_error,
rp.org_id, org.name AS org_name, c.resource_key, c.resource_label
FROM core.entitlement_set_change_obligations o
JOIN core.entitlement_set_changes c ON c.change_id = o.change_id
JOIN core.resource_pools rp ON rp.pool_id = o.pool_id
JOIN core.organizations org ON org.org_id = rp.org_id
WHERE c.set_id = $1 AND o.status = 'failed'
ORDER BY org.name ASC, o.pool_id ASC
LIMIT $2
`
type ListFailedObligationsParams struct {
SetID string `json:"set_id"`
Limit int32 `json:"limit"`
}
type ListFailedObligationsRow struct {
ChangeID string `json:"change_id"`
PoolID string `json:"pool_id"`
Attempts int32 `json:"attempts"`
LastAttemptAt sql.NullTime `json:"last_attempt_at"`
LastError sql.NullString `json:"last_error"`
OrgID string `json:"org_id"`
OrgName string `json:"org_name"`
ResourceKey sql.NullString `json:"resource_key"`
ResourceLabel sql.NullString `json:"resource_label"`
}
func (q *Queries) ListFailedObligations(ctx context.Context, arg ListFailedObligationsParams) ([]ListFailedObligationsRow, error) {
rows, err := q.db.QueryContext(ctx, listFailedObligations, arg.SetID, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ListFailedObligationsRow{}
for rows.Next() {
var i ListFailedObligationsRow
if err := rows.Scan(
&i.ChangeID,
&i.PoolID,
&i.Attempts,
&i.LastAttemptAt,
&i.LastError,
&i.OrgID,
&i.OrgName,
&i.ResourceKey,
&i.ResourceLabel,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listPendingObligations = `-- name: ListPendingObligations :many
SELECT change_id, pool_id, status, attempts, last_attempt_at, last_error, settled_at, created_at
FROM core.entitlement_set_change_obligations
WHERE change_id = $1 AND status = 'pending'
ORDER BY pool_id ASC
LIMIT $2
`
type ListPendingObligationsParams struct {
ChangeID string `json:"change_id"`
Limit int32 `json:"limit"`
}
// The drain's work list for one change. failed is the dead letter: the drain
// never picks one up again, and only RequeueFailedObligations, which the
// operator's Retry calls, returns it to pending.
func (q *Queries) ListPendingObligations(ctx context.Context, arg ListPendingObligationsParams) ([]EntitlementSetChangeObligation, error) {
rows, err := q.db.QueryContext(ctx, listPendingObligations, arg.ChangeID, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
items := []EntitlementSetChangeObligation{}
for rows.Next() {
var i EntitlementSetChangeObligation
if err := rows.Scan(
&i.ChangeID,
&i.PoolID,
&i.Status,
&i.Attempts,
&i.LastAttemptAt,
&i.LastError,
&i.SettledAt,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const markObligationFailed = `-- name: MarkObligationFailed :exec
SELECT core.settle_obligation(
$1::uuid,
$2::uuid,
'[]'::jsonb,
$3::text,
$4::integer
)
`
type MarkObligationFailedParams struct {
ChangeID string `json:"change_id"`
PoolID string `json:"pool_id"`
Failure string `json:"failure"`
MaxAttempts int32 `json:"max_attempts"`
}
// The failure half of the same function: no effect rows, the attempt and its
// error recorded, and the row failed once attempts reach max_attempts.
func (q *Queries) MarkObligationFailed(ctx context.Context, arg MarkObligationFailedParams) error {
_, err := q.db.ExecContext(ctx, markObligationFailed,
arg.ChangeID,
arg.PoolID,
arg.Failure,
arg.MaxAttempts,
)
return err
}
const requeueFailedObligations = `-- name: RequeueFailedObligations :exec
UPDATE core.entitlement_set_change_obligations o
SET status = 'pending', attempts = 0, last_error = NULL
FROM core.entitlement_set_changes c
WHERE c.change_id = o.change_id AND c.set_id = $1 AND o.status = 'failed'
`
// The Retry control: failed rows go back on the work list with their attempt
// count reset, so the drain gives each a fresh run of recomputeMaxAttempts.
func (q *Queries) RequeueFailedObligations(ctx context.Context, setID string) error {
_, err := q.db.ExecContext(ctx, requeueFailedObligations, setID)
return err
}
const settleObligation = `-- name: SettleObligation :one
SELECT core.settle_obligation(
$1::uuid,
$2::uuid,
$3::jsonb,
NULL
)::integer AS effects_written
`
type SettleObligationParams struct {
ChangeID string `json:"change_id"`
PoolID string `json:"pool_id"`
Effects json.RawMessage `json:"effects"`
}
// Records what one pool's recomputation did and marks the obligation. The
// calling transaction must hold the rendezvous in either mode.
func (q *Queries) SettleObligation(ctx context.Context, arg SettleObligationParams) (int32, error) {
row := q.db.QueryRowContext(ctx, settleObligation, arg.ChangeID, arg.PoolID, arg.Effects)
var effects_written int32
err := row.Scan(&effects_written)
return effects_written, err
}