Documentation
¶
Overview ¶
Package condition is the vocabulary an expression may use, and the shape of what it is told.
Two engines evaluate conditions - the rule engine and the retention sweep - and they read the same language (ADR-0009, automation.md §1.2, data-retention.md §2). This package is what makes that one statement: the names, and the projection of an entry into the document an expression reads. Two copies would be two dialects, and the day they diverged nobody would find out from a test.
The projection is deliberately narrow. Every field named here becomes part of what a rule written today may depend on, so adding one is a promise and removing one is a breaking change - which is why this is a written list rather than a reflection over the aggregate.
Index ¶
- Constants
- func CollectionOf(envelope event.Envelope) shared.ID
- func HubOf(envelope event.Envelope) shared.ID
- func ItemDocument(item work.WorkItem) map[string]any
- func ItemOf(envelope event.Envelope) shared.ID
- func JumbleEntryOf(envelope event.Envelope) shared.ID
- func LabelledItemDocument(item work.WorkItem, labels []string, members []string) map[string]any
- func PlaceOf(envelope event.Envelope) []identity.Scope
- func Placed(path []identity.Scope) bool
- func RetentionEnvironment() port.Environment
- func RuleEnvironment() port.Environment
- func WebhookEnvironment() port.Environment
- type Containers
- type Entries
- type JumbleEntries
- type Level
- type Location
- type Sets
- type Values
Constants ¶
const ( VarEvent = "event" VarItem = "item" VarParent = "parent" VarCollection = "collection" VarHub = "hub" VarActor = "actor" VarNow = "now" VarPayload = "payload" VarTenant = "tenant" // VarData is the CloudEvent's `data` - the event's payload - as a webhook subscriber receives // it. Only a webhook subscription's condition names it. VarData = "data" )
The names an expression may use. Written out rather than derived, because they are a contract: automation.md §1.2 lists them, and an expression naming anything else fails when it is written.
Variables ¶
This section is empty.
Functions ¶
func CollectionOf ¶
CollectionOf reads the collection out of an event's payload.
Two shapes, because there are two kinds of event. An item event names the collection it is in; a container event *is* the container, so a collection names itself and a hub names nothing - a hub-scoped rule matches the hub's own events through the hub identity below.
func HubOf ¶
HubOf reads a hub the event is directly about, which is the case a collection lookup cannot cover.
func ItemDocument ¶
ItemDocument is one entry as an expression reads it.
Snake case, because that is what the API answers and what somebody writing a condition has in front of them - `item.completed_at`, not `item.completedAt`. The names match the contract rather than the Go field, which is the whole reason this is a written projection.
Absent optional values are absent keys rather than nulls, so that `has(item.due_at)` answers what it looks like it answers. A null would make `item.due_at == null` and `!has(item.due_at)` two different questions with one answer.
func ItemOf ¶
itemOf reads the entry an event is about out of its subject.
The subject rather than the payload, because `<entity>/<id>` is the CloudEvents field that names what the event is about and every item event sets it. A payload key would be a second place to look, and the two would eventually disagree.
func JumbleEntryOf ¶
JumbleEntryOf reads the entry a jumble event is about out of its subject, ItemOf's shape.
func LabelledItemDocument ¶
LabelledItemDocument is ItemDocument with the sets an expression is most likely to ask about.
Separate, because the labels and members of an entry are not on the aggregate - they are read beside it - and a projection that pretended otherwise would answer an empty list for an entry whose labels nobody had loaded. An empty list and "not loaded" are the same value and different facts, and a condition on the wrong one of them is silently false.
func PlaceOf ¶
PlaceOf is where an event happened as the authoriser reads a path: the workspace, then the hub, collection or entry the event names. The authoriser completes a collection with its hub and reads an entry's hub from storage, so the payload's own identifiers are enough to ask whether somebody reaches the place - a private hub's events are kept from whoever does not (ADR-0073 §1). An event about nothing in a hub answers the workspace alone.
func RetentionEnvironment ¶
func RetentionEnvironment() port.Environment
RetentionEnvironment is what a retention rule's condition may name (data-retention.md §2).
A smaller set of the same names, and smaller on purpose rather than by omission. A retention pass is deciding about one entry against the clock; there is no event, no actor and no payload, because nobody caused it - the clock did. Declaring them anyway would let somebody write `actor.id == …` into a retention rule and get a condition that is never true.
func RuleEnvironment ¶
func RuleEnvironment() port.Environment
RuleEnvironment is what an automation rule's condition may name (automation.md §1.2).
`tenant` is a document rather than a name of its own, because the document writes `tenant.settings` - a variable called `tenant.settings` is not a name CEL can declare, and the settings are one field of the workspace rather than the whole of what a rule may know about it.
func WebhookEnvironment ¶
func WebhookEnvironment() port.Environment
WebhookEnvironment is what a webhook subscription's condition may name (automation.md §3.1).
Only what the dispatcher holds: the event, its `data` exactly as the subscriber will receive it, the actor and the moment. Not the entry, its collection or hub - the dispatcher runs in the integration context and does not read work management, and a condition that could name them would be answered by an absent document that nobody noticed was absent.
Types ¶
type Containers ¶
Containers is the read `collection` and `hub` need: a container by its identifier. Narrow rather than a repository, so that nothing holding an activation can write through it.
type JumbleEntries ¶
JumbleEntries is the read `payload` costs on a jumble run: the entry, by its identifier. Narrow for the reason every lookup here is.
type Level ¶
type Level string
Level is how far down a scope reaches: the workspace, one hub, or one collection.
type Location ¶
Location is the hub and collection an event happened in, as far as it can be said. An event about neither - a rule run, an arrival in the jumble - carries none, and nothing scoped below the workspace matches it: something scoped to one hub must not act on a workspace-wide event because nothing said otherwise. Shared by the rules and the webhook subscriptions, which scope alike (automation.md §1, §3.1).
func Locate ¶
Locate reads the event's collection and the hub above it.
The collection comes from the payload, which every item event carries (`collection_id`) and every container event carries as its own identity. The hub is one read, and only when the event is in a collection at all.
func (Location) Path ¶
Path is the place as the authoriser reads it; a collection without its hub is completed there.
type Sets ¶
Sets is the read `item.labels` and `item.members` need: the identifiers an entry carries, which live beside the aggregate rather than on it. Narrow, like Entries, so that nothing holding an activation can write through it; the label and the member repositories both fit.
type Values ¶
type Values struct {
Envelope event.Envelope
// now is the run's single reading, passed rather than taken: an expression evaluated twice in
// one run sees one instant (automation.md §1.2).
Now time.Time
// subject is the entry a run that no event started is about - a RELATIVE_DATE run measured
// from one entry's due date. The envelope's subject wins where there is one, so an
// event-triggered run is unaffected by this field existing.
Subject shared.ID
// payload is the body an inbound delivery carried. Untrusted from end to end: it is read as
// *data* under one name and never rendered as an instruction to anything (ai-first.md §1.3,
// automation.md §1.1).
Payload map[string]any
Entries Entries
Containers Containers
// Labels and Members answer the entry's two sets beside it, so that `item.labels` and
// `item.members` are the lists automation.md §1 promises rather than absent keys.
// Read only when `item` is - the same laziness - and optional: without them the document is
// the entry alone.
Labels Sets
Members Sets
// JumbleID names the entry a JUMBLE_ENTRY run is about; `payload` is rendered from it, lazily
// and as data. Zero everywhere else - the envelope's own subject still lets an EVENT
// rule on a jumble event read the same names.
JumbleID shared.ID
Jumble JumbleEntries
}
Values is what an expression is told about one event, resolved when it asks.
The laziness is the port's promise and the reason the engine can afford conditions at all: a condition naming only `event` costs no reads, and one naming `item` costs one. The engine evaluates every enabled rule against every event, so eagerly building `collection`, `hub` and `parent` would turn one event into four queries per rule (automation.md §1.2).
The lookups are optional. A caller that has none - the subscriber rendering a dedupe key inside the dispatcher's transaction, which may not read the work management context - answers `item` as absent rather than failing, and an expression that names it sees a value that is not there.
func (Values) EntryID ¶
EntryID is entry for a caller outside the expressions: the engine, which supplies the entry to the actions beside the event (automation.md §2.2), so that the actions and the conditions of one run cannot be about two different entries. Zero for a run that is about none - a container event, a jumble arrival, a schedule.