Documentation
¶
Overview ¶
Package interests defines the shared "what events do you want?" model used by both Slack channel subscriptions and webhooks.
One JSONB shape is stored on both subscriber tables. One classifier turns a signal lifecycle event into a list of slugs (resource:..., op:..., outcome:..., event:...) and one matcher decides whether a given Interests config wants to receive that event. UI variants (slack vs webhook) collapse/expand the same underlying fields differently but persist identically.
AllEvents is the new-subscription default; once a user opts into per-resource configuration the Resources map becomes the source of truth and AllEvents must be false.
Index ¶
- Constants
- Variables
- func Classify(event signal.SignalPhaseEvent, outcome *signal.SignalPhaseOutcome, db *gorm.DB) []string
- func Matches(event signal.SignalPhaseEvent, outcome *signal.SignalPhaseOutcome, db *gorm.DB, ...) bool
- func OpSlug(kind ResourceKind, op string) string
- func ResourceSlug(kind ResourceKind) string
- func SupportsDriftDetected(kind ResourceKind) bool
- func Validate(in Interests) error
- type Interests
- type Outcome
- type ResourceCfg
- type ResourceKind
Constants ¶
const ( SlugPrefixResource = "resource:" SlugPrefixOp = "op:" SlugPrefixOutcome = "outcome:" SlugPrefixEvent = "event:" )
Slug prefix constants. Webhook payloads embed a top-level `interests` array of these slugs so consumers can route by prefix without re-implementing the classifier.
const ( SlugOutcomeCompletion = SlugPrefixOutcome + "completion" SlugOutcomeFailures = SlugPrefixOutcome + "failures" )
Outcome slugs. outcome:completion is emitted on terminal events of any status; outcome:failures is emitted only when the terminal status is failed/cancelled.
const ( SlugEventLifecycleStarted = SlugPrefixEvent + "lifecycle.started" SlugEventLifecycleSucceeded = SlugPrefixEvent + "lifecycle.succeeded" SlugEventLifecycleFailed = SlugPrefixEvent + "lifecycle.failed" SlugEventLifecycleCancelled = SlugPrefixEvent + "lifecycle.cancelled" )
Lifecycle event slugs (workflow / step transitions).
const ( SlugEventApprovalRequest = SlugPrefixEvent + "approval.request" SlugEventApprovalResponse = SlugPrefixEvent + "approval.response" SlugEventApprovalResponseApproved = SlugPrefixEvent + "approval.response.approved" SlugEventApprovalResponseRejected = SlugPrefixEvent + "approval.response.rejected" )
Approval handshake slugs. event:approval.response is emitted as a generic fallback when the specific approved/rejected outcome cannot be resolved (e.g. classify() called without a DB). The webhook hook upgrades the payload's interests array to the more specific form when it has the data.
const (
SlugEventDriftDetected = SlugPrefixEvent + "drift.detected"
)
Drift detection slug. event:drift.detected is emitted by the drift-detected signal that fires from inside the plan-only check of a drift_run / drift_run_reprovision_sandbox workflow when the plan has changes (i.e. drift was actually found). Subscribers opt in via the per-resource `drift_detected` flag, which is gated independently of `outcome`.
Variables ¶
var AllResources = []ResourceKind{ ResourceInstalls, ResourceComponents, ResourceSandboxes, ResourceInstallConfigurations, ResourceRunners, ResourceActions, }
AllResources is the canonical, ordered list of resource kinds. UI code renders rows in this order; defaults() / docs walk it.
var SubOps = map[ResourceKind][]string{ ResourceInstalls: {"provision", "deprovision", "reprovision"}, ResourceComponents: {"deploy", "teardown"}, ResourceSandboxes: {"provision", "reprovision", "deprovision"}, ResourceInstallConfigurations: {"inputs", "secrets"}, ResourceRunners: {"provision", "reprovision", "inactive"}, ResourceActions: {"run"}, }
SubOps is the canonical sub-op vocabulary per resource. The classifier maps real WorkflowType constants from internal/app/workflow.go onto these slugs. UIs render checkbox lists from this map.
Note: "drift" is intentionally NOT listed for components or sandboxes even though the classifier still emits the slug. Drift workflow lifecycle events are pure noise (one started/completed pair per cron tick per resource) and are unconditionally suppressed in match.go. Subscribers opt into drift notifications through DriftDetected, which gates the dedicated drift-detected event that only fires when the plan-only check observes actual changes.
Functions ¶
func Classify ¶
func Classify(event signal.SignalPhaseEvent, outcome *signal.SignalPhaseOutcome, db *gorm.DB) []string
Classify returns the slug list for a single event. The webhook hook stamps these onto the outbound payload's top-level `interests` array so consumers can route by prefix without re-implementing the classifier.
outcome may be nil (BeforePhase / "started" emission). db may be nil — when nil, classification falls back to the WorkflowType-only path and skips step / approval-response disambiguation.
func Matches ¶
func Matches(event signal.SignalPhaseEvent, outcome *signal.SignalPhaseOutcome, db *gorm.DB, in Interests) bool
Matches reports whether the given Interests config wants to receive this lifecycle event. Hooks (Slack channel sub, webhook) call this once per subscription before dispatching.
Semantics:
- AllEvents=true: matches every classifiable event.
- Empty Resources (and AllEvents=false): never matches.
- Resource missing from Resources: never matches.
- Resource present:
- Ops empty → every sub-op for this resource matches.
- Ops non-empty → only the listed sub-op matches.
- Approval events are gated by ApprovalRequests / ApprovalResponses (independent of Outcome).
- Lifecycle events apply Outcome:
- "" / "all" → every started + terminal event.
- "completion" → terminal events only (suppress started).
- "failures" → only failed/cancelled terminal events.
outcome may be nil at BeforePhase (started). db may be nil — when nil, classification falls back to the WorkflowType-only path which is enough for execute-workflow events but skips step-scoped enrichment.
func ResourceSlug ¶
func ResourceSlug(kind ResourceKind) string
ResourceSlug returns "resource:<kind>".
func SupportsDriftDetected ¶
func SupportsDriftDetected(kind ResourceKind) bool
SupportsDriftDetected reports whether DriftDetected is meaningful for the given resource. Currently true for components and sandboxes — the only resources whose workflows can produce a drift-detected event.
func Validate ¶
Validate checks that resource keys + ops + outcome on an Interests config match the canonical taxonomy declared in this package. Empty configs and AllEvents=true are both valid; the picker UI is the place where shape is enforced visually, but the API still refuses outright garbage.
Used by both the slack channel subscription create handler and the webhook create/update handlers. Lifted from internal/app/slack/service so both surfaces share one implementation.
Types ¶
type Interests ¶
type Interests struct {
AllEvents bool `json:"all_events,omitempty"`
Resources map[ResourceKind]ResourceCfg `json:"resources,omitempty"`
}
Interests is the full per-subscriber config. Stored as JSONB on both slack_channel_subscriptions and webhooks.
AllEvents acts as a sentinel: when true, every supported event matches regardless of Resources content. Toggling AllEvents off in the picker materializes Default() (the per-resource opt-out baseline) into Resources and flips AllEvents to false.
func AllEvents ¶
func AllEvents() Interests
AllEvents returns the new-subscription default: a sentinel Interests config that matches every supported lifecycle and approval event. This is what the "Send all events" toggle in the picker writes when it's checked, and what new webhooks/slack subs default to.
func Default ¶
func Default() Interests
Default returns the materialised "power user opted out of AllEvents" baseline. Four resources (installs, components, sandboxes, install_configurations) are present with empty ops (= all sub-ops), Outcome=Completion, and approval flags both true. Runners + actions are absent (= off).
Toggling "Send all events" off in the picker writes this exact shape so users land on a sensible per-resource starting point instead of an empty config that silently drops every event.
func (Interests) GormDataType ¶
GormDataType tells GORM to materialise the column as JSONB.
func (Interests) IsZero ¶
IsZero is true for the zero-value Interests (no AllEvents, no resources). Used by callers to distinguish "never configured" from "explicitly empty".
type Outcome ¶
type Outcome string
Outcome filters lifecycle events on terminal status. Approval events are gated separately by ResourceCfg.ApprovalRequests / ApprovalResponses and ignore this field.
const ( // OutcomeAll forwards every started + terminal lifecycle event. OutcomeAll Outcome = "all" // OutcomeCompletion forwards only terminal events (succeeded / failed / // cancelled). Started events are suppressed. OutcomeCompletion Outcome = "completion" // OutcomeFailures forwards only failed / cancelled terminal events. OutcomeFailures Outcome = "failures" )
type ResourceCfg ¶
type ResourceCfg struct {
Ops []string `json:"ops,omitempty"`
Outcome Outcome `json:"outcome,omitempty"`
ApprovalRequests bool `json:"approval_requests,omitempty"`
ApprovalResponses bool `json:"approval_responses,omitempty"`
DriftDetected bool `json:"drift_detected,omitempty"`
}
ResourceCfg is the per-resource filter. An empty Ops slice means "every sub-op for this resource". Outcome=="" is treated as OutcomeAll.
ApprovalRequests / ApprovalResponses are independent of Outcome — approval events do not have a started/succeeded/failed lifecycle, only a requested / approved / rejected handshake.
DriftDetected is independent of Outcome too. It gates the drift_detected event that fires from inside the plan-only check of a drift_run / drift_run_reprovision_sandbox workflow when the plan has changes. Only meaningful for resources where SupportsDriftDetected returns true (components, sandboxes); set on other resources is harmless but never matches. Drift workflow lifecycle events themselves are unconditionally suppressed by the matcher — DriftDetected is the only knob that surfaces drift to subscribers.
LOCKSTEP: changes to this struct's JSON shape must also update bins/cli/cmd/orgs.go (interestsJSONHelp) and the "Filtering events with interests" section of docs/guides/webhooks.mdx.
type ResourceKind ¶
type ResourceKind string
ResourceKind is the top-level event taxonomy exposed in the picker UI. The classifier maps each lifecycle / approval event to exactly one ResourceKind.
const ( ResourceInstalls ResourceKind = "installs" ResourceComponents ResourceKind = "components" ResourceSandboxes ResourceKind = "sandboxes" ResourceInstallConfigurations ResourceKind = "install_configurations" ResourceRunners ResourceKind = "runners" ResourceActions ResourceKind = "actions" )