Documentation
¶
Overview ¶
Package pubinterface is ADR-0373's published process interface: a small, versioned, explicitly published contract a process offers another domain, derived from a deployed definition rather than typed out beside it, with the right to read it (view) and the right to deliver to it (send) granted apart.
It is design-time configuration, like a start grant (ADR-0516) or a release: the store is a sidecar (ADR-0282) owned by the API run loop, and nothing here reaches the processor, the WAL or applyToState. Deriving a contract reads a compiled process, which is immutable and safe to read from any goroutine.
What this package does not do yet, and says so rather than pretending:
- a commitment (by when an accepted message will be correlated) is refused at publish, because nothing measures it yet and a promise nobody watches reads as kept;
- the send grant is answered by Store.MaySend for the peer endpoint of ADR-0372, which is not built; until it is, a send grant lets nobody deliver anything;
- the observe grant is deliberately absent, as ADR-0373 decided.
Index ¶
- Constants
- Variables
- func Realize(c Candidates, processID string, definitionKey uint64, version int32, ...) (Contract, Binding, []string)
- func ValidID(s string) bool
- func ValidName(s string) bool
- type Audience
- type Binding
- type CandidateEntryPoint
- type CandidateOutcome
- type CandidateReturns
- type Candidates
- type CandidatesView
- type Change
- type Contract
- type Definition
- type ElementMap
- type EntryPoint
- type Grants
- type Host
- type Interface
- type InterfaceView
- type Meta
- type MutationView
- type Outcome
- type Payload
- type Pick
- type Principal
- type Returns
- type Selection
- type Service
- func (s *Service) HandleCandidates(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleDeprecate(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleGet(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleGetVersion(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleHistory(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleList(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandlePublish(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleRebind(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleSetGrants(w http.ResponseWriter, r *http.Request)
- func (s *Service) Store() *Store
- type Store
- func (s *Store) All() []Interface
- func (s *Store) Deprecate(id string, version int, note string, m Meta) (Interface, error)
- func (s *Store) Get(id string) (Interface, bool)
- func (s *Store) MaySend(id string, version int, p Principal) (Binding, bool)
- func (s *Store) Publish(id, applicationID string, contract Contract, binding Binding, m Meta) (Interface, error)
- func (s *Store) Rebind(id string, version int, binding Binding, m Meta) (Interface, error)
- func (s *Store) SetGrants(id string, g Grants, m Meta) (Interface, error)
- func (s *Store) Uncertain() bool
- type SummaryView
- type Version
- type VersionView
Constants ¶
const ( KindMessageStart = "message-start" // a root-scope message start event (ADR-0035) KindIntermediateCatch = "intermediate-catch" // a message intermediate catch event (ADR-0020) KindReceiveTask = "receive-task" // a receive task (ADR-0102) KindEventSubprocess = "event-subprocess" // a message-triggered event subprocess (ADR-0082) KindBoundary = "boundary" // a message boundary event (ADR-0040) )
Entry-point kinds: the element behind a contract-level entry point.
const ( // BasisInformationModel: the element writes the message into a data object typed // against the information model (ADR-0230); Class names that class. BasisInformationModel = "information-model" // BasisFields: the element's output mappings read these names. A name may be a // field of the message or a variable the instance already holds — the model does // not say which, and the descriptor does not guess. BasisFields = "fields" // BasisUntyped: the model states no shape at all; the message's variables merge // into the instance as sent. The descriptor says so rather than inventing one. BasisUntyped = "untyped" )
Payload bases: where a derived payload description came from.
const ( OutcomeEnd = "end" OutcomeError = "error" OutcomeEscalation = "escalation" OutcomeMessage = "message" OutcomeSignal = "signal" OutcomeTerminate = "terminate" OutcomeCompensation = "compensation" )
Outcome kinds: how a root-scope end event ends an instance.
const ( RankNone = 0 RankViewer = 1 RankEditor = 2 RankOwner = 3 )
Ranks of the caller's role on the publishing application (ADR-0071).
const ( )
Availability reasons: why a contract version cannot be delivered to now. The contract stays readable either way, so a consumer's binding stays legible.
const ( ActionPublish = "publish" ActionRebind = "rebind" ActionDeprecate = "deprecate" ActionGrants = "grants" )
Audited actions. Each change to an interface — a contract published, a version rebound or deprecated, a grant changed — is one immutable entry in its history, in the sense ADR-0184 established for rights.
const SchemaVersion = 1
SchemaVersion identifies the durable interface-document format.
Variables ¶
var ( ErrStaleRevision = errors.New("published interface revision changed") ErrUncertainWrite = errors.New("published interface store requires recovery after write error") ErrNotFound = errors.New("no published interface with that id") ErrNoVersion = errors.New("no such contract version") ErrOtherProcess = errors.New("the interface belongs to another process") ErrSameContract = errors.New("the contract is the newest version's; rebind it instead of publishing it again") ErrTooManyVersions = errors.New("the interface holds the most contract versions it may") ErrDeprecated = errors.New("the version is already deprecated") )
Errors a write can answer.
Functions ¶
func Realize ¶
func Realize(c Candidates, processID string, definitionKey uint64, version int32, sel Selection) (Contract, Binding, []string)
Realize turns a selection over a definition's candidates into the contract it publishes and the binding that realizes it. Problems are the reasons it cannot, in words a publisher can act on; when there are any, the contract is not usable.
Types ¶
type Audience ¶
Audience is who a grant names: user ids and group ids of this server's directory, never display names.
func CheckAudience ¶
CheckAudience refuses an oversized, malformed or repeating audience, and answers it sorted, so a grant is stored and compared in one canonical order.
type Binding ¶
type Binding struct {
ProcessID string `json:"processId"`
DefinitionKey uint64 `json:"definitionKey"`
DefinitionVersion int32 `json:"definitionVersion"`
EntryPoints []ElementMap `json:"entryPoints"`
Outcomes []ElementMap `json:"outcomes"`
}
Binding is the mutable half of a contract version: which deployed definition realizes it now, and through which elements. Rebinding to a newer definition is allowed exactly when that definition still realizes the same contract.
type CandidateEntryPoint ¶
type CandidateEntryPoint struct {
ElementID string `json:"elementId"`
ElementName string `json:"elementName,omitempty"`
Kind string `json:"kind"`
Starts bool `json:"starts"`
MessageName string `json:"messageName"`
CorrelationKey string `json:"correlationKey,omitempty"`
Payload Payload `json:"payload"`
}
CandidateEntryPoint is one element a message can reach, offered for publication.
type CandidateOutcome ¶
type CandidateOutcome struct {
ElementID string `json:"elementId"`
ElementName string `json:"elementName,omitempty"`
Kind string `json:"kind"`
}
CandidateOutcome is one root-scope end event: a terminal outcome an instance can reach. Publishing names every one of them, because a contract that lists some outcomes would tell a consumer an instance cannot end in the others.
type CandidateReturns ¶
type CandidateReturns struct {
Errors []string `json:"errors"`
Escalations []string `json:"escalations"`
// ThrownMessages are the names the definition's message throw and end events
// send. Which of them are replies a consumer may receive is the publisher's to
// say: a throw to another process of the same domain is no part of a contract.
ThrownMessages []string `json:"thrownMessages"`
}
CandidateReturns are the codes that can leave an instance uncaught, and the messages it throws — from which the publisher picks the ones that are replies.
type Candidates ¶
type Candidates struct {
EntryPoints []CandidateEntryPoint `json:"entryPoints"`
Outcomes []CandidateOutcome `json:"outcomes"`
Returns CandidateReturns `json:"returns"`
}
Candidates is everything a definition offers for publication.
func Derive ¶
func Derive(cp *compiler.CompiledProcess, names map[string]string) Candidates
Derive reads the candidates out of a compiled process. names maps a BPMN element id to its name, for display only; it may be nil.
type CandidatesView ¶
type CandidatesView struct {
DefinitionKey uint64 `json:"definitionKey"`
ProcessID string `json:"processId"`
Version int32 `json:"version"`
ApplicationID string `json:"applicationId"`
Candidates
// Interfaces are the published interfaces of this process already, so a
// publisher adds a version rather than a second interface by mistake.
Interfaces []string `json:"interfaces"`
}
CandidatesView is what a definition offers for publication.
type Change ¶
type Change struct {
Revision uint64 `json:"revision"`
At int64 `json:"at"`
ActorID string `json:"actorId"`
Action string `json:"action"`
// Version is the contract version the change concerns; zero for a grant change.
Version int `json:"version,omitempty"`
// DefinitionKey is the definition a publish or rebind bound the version to.
DefinitionKey uint64 `json:"definitionKey,omitempty"`
// Grants is the whole of both rights as the change left them, for a grant change:
// an audit that said only that something changed could not answer who could send.
Grants *Grants `json:"grants,omitempty"`
Note string `json:"note,omitempty"`
}
Change is one audited mutation of an interface.
type Contract ¶
type Contract struct {
EntryPoints []EntryPoint `json:"entryPoints"`
Returns Returns `json:"returns"`
Outcomes []Outcome `json:"outcomes"`
}
Contract is the immutable part of a contract version: what a consumer binds to. Changing any of it means publishing a new version.
func (Contract) Differences ¶
Differences names, in plain words, what separates two contracts — what a publisher reads when a rebinding is refused.
type Definition ¶
type Definition struct {
Key uint64
ProcessID string
Version int32
ApplicationID string
Inactive bool
Process *compiler.CompiledProcess
}
Definition is what the server knows about one deployed definition.
type ElementMap ¶
type ElementMap struct {
Name string `json:"name"`
ElementIDs []string `json:"elementIds"`
MessageName string `json:"messageName,omitempty"`
}
ElementMap ties one contract name to the element(s) behind it in the bound definition. MessageName is the message an entry point's element waits for: an internal name, which the contract never shows, and which a refactoring may change.
type EntryPoint ¶
type EntryPoint struct {
Name string `json:"name"`
Kind string `json:"kind"`
// Starts is true when a message here creates an instance, false when it
// continues one that is already waiting.
Starts bool `json:"starts"`
// CorrelationKey is the FEEL expression a message is correlated by, as authored;
// empty for a start that states none.
CorrelationKey string `json:"correlationKey,omitempty"`
Payload Payload `json:"payload"`
}
EntryPoint is one contract-level way into the published process. Its Name is the contract; the element behind it is the binding's business and never appears here.
type Grants ¶
type Grants struct {
Public bool `json:"public"`
View Audience `json:"view"`
Send Audience `json:"send"`
}
Grants are an interface's two rights, kept apart on purpose (ADR-0373): seeing a contract never lets anybody drive work into the publishing domain. Public means "viewable without a grant" and never implies send.
type Host ¶
type Host struct {
Definition func(key uint64) (Definition, bool)
Rank func(p *httpapi.Principal, applicationID string) (int, error)
UserExists func(id string) (bool, error)
GroupExists func(id string) (bool, error)
Names func(key uint64) map[string]string
}
Host is what the server supplies. Definition, Rank, UserExists and GroupExists are called on the run loop only; Names is called off it.
type Interface ¶
type Interface struct {
SchemaVersion int `json:"schemaVersion"`
ID string `json:"id"`
// ProcessID is the BPMN process the interface belongs to, fixed at its first
// publication. ApplicationID is the application that process was filed under
// then; every later act must find the bound definition filed under it too.
ProcessID string `json:"processId"`
ApplicationID string `json:"applicationId"`
Revision uint64 `json:"revision"`
Versions []Version `json:"versions"`
Grants Grants `json:"grants"`
History []Change `json:"history"`
}
Interface is one published interface as the store holds it.
func (Interface) MaySend ¶
MaySend reports whether a principal may deliver to the interface's entry points. Public never implies it, and neither does view.
type InterfaceView ¶
type InterfaceView struct {
ID string `json:"id"`
LatestVersion int `json:"latestVersion"`
Public bool `json:"public"`
MayView bool `json:"mayView"`
MaySend bool `json:"maySend"`
Versions []VersionView `json:"versions"`
// Member fields: the publishing side's own view.
ProcessID string `json:"processId,omitempty"`
ApplicationID string `json:"applicationId,omitempty"`
Revision uint64 `json:"revision,omitempty"`
Grants *Grants `json:"grants,omitempty"`
}
InterfaceView is one interface as a reader sees it.
type Meta ¶
Meta is what every write is stamped with: the actor and the time are the server's, never the client's, and ExpectedRevision is the revision the actor read (zero for an interface that does not exist yet).
type MutationView ¶
type MutationView struct {
InterfaceView
Version int `json:"version"`
}
MutationView is a write's answer: the interface as it now stands, and the version the write concerned.
type Outcome ¶
Outcome is one terminal outcome an instance can reach, under a name the publisher assigned. Several end events may share one outcome; they must share its kind.
type Payload ¶
type Payload struct {
Basis string `json:"basis"`
Class string `json:"class,omitempty"`
Fields []string `json:"fields,omitempty"`
}
Payload is what an entry point expects, as far as the model states it.
type Pick ¶
type Pick struct {
Name string `json:"name"`
ElementID string `json:"elementId,omitempty"`
ElementIDs []string `json:"elementIds,omitempty"`
}
Pick names one element (or, for an outcome, several end events sharing it).
type Returns ¶
type Returns struct {
Errors []string `json:"errors"`
Escalations []string `json:"escalations"`
Messages []string `json:"messages"`
}
Returns are what the publisher promises may come back to a consumer: the error and escalation codes that can leave an instance uncaught, and the reply messages the publisher chose to name.
type Selection ¶
type Selection struct {
EntryPoints []Pick `json:"entryPoints"`
Outcomes []Pick `json:"outcomes"`
Replies []string `json:"replies"`
}
Selection is what a publisher chooses: which elements become which entry points, what every terminal outcome is called, and which thrown messages are replies.
func SelectionOf ¶
SelectionOf answers the selection a binding made, so a rebinding can reuse the element ids a refactoring did not touch.
type Service ¶
type Service struct {
// Limits are the installation's resource budgets (ADR-0291); the server
// overwrites the default with its own.
Limits limits.Limits
// contains filtered or unexported fields
}
Service serves the published-interface routes. Build it with NewService.
func NewService ¶
func NewService(loop *runloop.Loop, store *Store, host Host, authEnabled func() bool, now func() int64) *Service
NewService builds the service. authEnabled is read per request because the server settles it after constructing its services; now stamps an audit entry in Unix seconds.
func (*Service) HandleCandidates ¶
func (s *Service) HandleCandidates(w http.ResponseWriter, r *http.Request)
HandleCandidates answers what one deployed definition offers for publication: its entry points, every terminal outcome, the codes that can leave it and the messages it throws. It reads the model, so the application's viewers may ask.
func (*Service) HandleDeprecate ¶
func (s *Service) HandleDeprecate(w http.ResponseWriter, r *http.Request)
HandleDeprecate marks one contract version deprecated (the vocabulary ADR-0130 has for process versions). The version stays readable and deliverable; deprecation tells a consumer to move.
func (*Service) HandleGet ¶
func (s *Service) HandleGet(w http.ResponseWriter, r *http.Request)
HandleGet answers one interface with every contract version.
func (*Service) HandleGetVersion ¶
func (s *Service) HandleGetVersion(w http.ResponseWriter, r *http.Request)
HandleGetVersion answers one contract version.
func (*Service) HandleHistory ¶
func (s *Service) HandleHistory(w http.ResponseWriter, r *http.Request)
HandleHistory answers the interface's audit, newest first. It names the people who published, rebound and granted, and every audience a grant change left, so like a project's grant audit (ADR-0184) it is the publishing application's owner's to read.
func (*Service) HandleList ¶
func (s *Service) HandleList(w http.ResponseWriter, r *http.Request)
HandleList answers every interface the caller may see, ordered by id — and only those: an interface the caller may not view is not listed, so its existence is not disclosed either (ADR-0373).
func (*Service) HandlePublish ¶
func (s *Service) HandlePublish(w http.ResponseWriter, r *http.Request)
HandlePublish publishes a new contract version derived from one deployed definition, creating the interface on its first version. The publisher names which elements are entry points and what every terminal outcome is called; kind, correlation key, payload, errors and escalations are read from the model. The caller needs an editor's role on the application the definition is filed under.
func (*Service) HandleRebind ¶
func (s *Service) HandleRebind(w http.ResponseWriter, r *http.Request)
HandleRebind points one contract version at another deployed definition of the same process — the publisher's refactoring behind an unchanged contract. The new definition must realize exactly the version's contract; the element mapping defaults to the current one and may be given anew where an element was renamed. A contract that differs is refused with what differs: that is a new version.
func (*Service) HandleSetGrants ¶
func (s *Service) HandleSetGrants(w http.ResponseWriter, r *http.Request)
HandleSetGrants wholly replaces who may view and who may send. It is the application owner's act, from their own signed-in session, as sharing an application is: a send grant lets another domain drive work into this one.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store owns the published interfaces, with single-writer run-loop access. Never call it concurrently or from the processor hot path.
func NewStore ¶
NewStore loads every interface document and validates it. A corrupt document fails startup rather than granting from partial data.
func (*Store) Deprecate ¶
Deprecate marks one contract version deprecated. It stays readable and bound — deprecation tells a consumer to move, it does not take anything away — and it is not undone: a consumer that moved must not be told to move back.
func (*Store) Get ¶
Get answers a defensive copy of one interface. Nothing is answered while the store is uncertain.
func (*Store) MaySend ¶
MaySend answers the binding of one contract version when the principal holds the interface's send grant, for the peer endpoint of ADR-0372 to deliver through. It does not know whether the bound definition is still active; the caller checks.
func (*Store) Publish ¶
func (s *Store) Publish(id, applicationID string, contract Contract, binding Binding, m Meta) (Interface, error)
Publish adds a new contract version, creating the interface on its first one. The caller has checked that the actor may publish the process and derived the contract and its binding from a deployed definition.
func (*Store) Rebind ¶
Rebind points one contract version at another definition. The caller has derived the contract that definition realizes and found it equal to the version's own: the contract is immutable, the binding is what a refactoring moves.
type SummaryView ¶
type SummaryView struct {
ID string `json:"id"`
LatestVersion int `json:"latestVersion"`
Deprecated bool `json:"deprecated"`
Available bool `json:"available"`
EntryPoints []string `json:"entryPoints"`
Public bool `json:"public"`
MaySend bool `json:"maySend"`
ProcessID string `json:"processId,omitempty"`
ApplicationID string `json:"applicationId,omitempty"`
}
SummaryView is one interface in the list.
type Version ¶
type Version struct {
Version int `json:"version"`
PublishedAt int64 `json:"publishedAt"`
PublishedBy string `json:"publishedBy"`
Contract Contract `json:"contract"`
Binding Binding `json:"binding"`
BoundAt int64 `json:"boundAt"`
BoundBy string `json:"boundBy"`
Deprecated bool `json:"deprecated,omitempty"`
// DeprecatedAt, DeprecatedBy and DeprecationNote say when, by whom and why, so a
// consumer told at deploy can read what to move to.
DeprecatedAt int64 `json:"deprecatedAt,omitempty"`
DeprecatedBy string `json:"deprecatedBy,omitempty"`
DeprecationNote string `json:"deprecationNote,omitempty"`
}
Version is one published contract version: the immutable contract, the binding that realizes it now, and whether the publisher deprecated it.
type VersionView ¶
type VersionView struct {
Version int `json:"version"`
PublishedAt int64 `json:"publishedAt"`
Deprecated bool `json:"deprecated"`
DeprecatedAt int64 `json:"deprecatedAt,omitempty"`
DeprecationNote string `json:"deprecationNote,omitempty"`
Available bool `json:"available"`
Contract Contract `json:"contract"`
// The fields below are the publisher's internal organisation, answered only to
// the publishing application's own members.
PublishedBy string `json:"publishedBy,omitempty"`
Binding *Binding `json:"binding,omitempty"`
BoundAt int64 `json:"boundAt,omitempty"`
BoundBy string `json:"boundBy,omitempty"`
}
VersionView is one contract version as a reader sees it.