pubinterface

package
v0.25.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 9, 2026 License: AGPL-3.0 Imports: 17 Imported by: 0

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

View Source
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.

View Source
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.

View Source
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.

View Source
const (
	RankNone   = 0
	RankViewer = 1
	RankEditor = 2
	RankOwner  = 3
)

Ranks of the caller's role on the publishing application (ADR-0071).

View Source
const (
	UnavailableDefinitionMissing  = "definition-missing"
	UnavailableDefinitionInactive = "definition-inactive"
	UnavailableOtherApplication   = "definition-in-other-application"
)

Availability reasons: why a contract version cannot be delivered to now. The contract stays readable either way, so a consumer's binding stays legible.

View Source
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.

View Source
const SchemaVersion = 1

SchemaVersion identifies the durable interface-document format.

Variables

View Source
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.

func ValidID

func ValidID(s string) bool

ValidID reports whether s is a usable interface id: a lower-case slug such as "kreditpruefung", at most 64 characters.

func ValidName

func ValidName(s string) bool

ValidName reports whether s is a usable contract-level name for an entry point or an outcome: a letter, then letters, digits, '_', '.' or '-', at most 128.

Types

type Audience

type Audience struct {
	UserIDs  []string `json:"userIds"`
	GroupIDs []string `json:"groupIds"`
}

Audience is who a grant names: user ids and group ids of this server's directory, never display names.

func CheckAudience

func CheckAudience(a Audience) (Audience, error)

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

func (c Contract) Differences(o Contract) []string

Differences names, in plain words, what separates two contracts — what a publisher reads when a rebinding is refused.

func (Contract) Equal

func (c Contract) Equal(o Contract) bool

Equal reports whether two contracts promise the same thing.

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) Latest

func (in Interface) Latest() Version

Latest answers the newest contract version; an interface always has one.

func (Interface) MaySend

func (in Interface) MaySend(p Principal) bool

MaySend reports whether a principal may deliver to the interface's entry points. Public never implies it, and neither does view.

func (Interface) MayView

func (in Interface) MayView(p Principal) bool

MayView reports whether a principal may read the contract: anybody when it is public, else the view audience. It does not consider the publishing application's own members; the service adds those, because only it knows the application.

func (Interface) VersionOf

func (in Interface) VersionOf(v int) (Version, bool)

VersionOf answers one contract version.

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

type Meta struct {
	ActorID          string
	At               int64
	ExpectedRevision uint64
}

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

type Outcome struct {
	Name string `json:"name"`
	Kind string `json:"kind"`
}

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 Principal

type Principal struct {
	UserID   string
	GroupIDs []string
}

Principal is the caller a grant is checked against.

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

func SelectionOf(b Binding, replies []string) Selection

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.

func (*Service) Store

func (s *Service) Store() *Store

Store answers the store, for the peer endpoint of ADR-0372 to check a send grant against once it exists.

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

func NewStore(dir string) (*Store, error)

NewStore loads every interface document and validates it. A corrupt document fails startup rather than granting from partial data.

func (*Store) All

func (s *Store) All() []Interface

All answers every interface, ordered by id.

func (*Store) Deprecate

func (s *Store) Deprecate(id string, version int, note string, m Meta) (Interface, error)

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

func (s *Store) Get(id string) (Interface, bool)

Get answers a defensive copy of one interface. Nothing is answered while the store is uncertain.

func (*Store) MaySend

func (s *Store) MaySend(id string, version int, p Principal) (Binding, bool)

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

func (s *Store) Rebind(id string, version int, binding Binding, m Meta) (Interface, error)

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.

func (*Store) SetGrants

func (s *Store) SetGrants(id string, g Grants, m Meta) (Interface, error)

SetGrants wholly replaces both rights and the public flag.

func (*Store) Uncertain

func (s *Store) Uncertain() bool

Uncertain reports whether a failed write left the store's durable state unknown. While it does, every positive answer and every further write is refused until a restart re-reads what actually reached the disk.

type SummaryView

type SummaryView struct {
	ID                 string   `json:"id"`
	LatestVersion      int      `json:"latestVersion"`
	Deprecated         bool     `json:"deprecated"`
	Available          bool     `json:"available"`
	UnavailableReasons []string `json:"unavailableReasons"`
	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"`
	UnavailableReasons []string `json:"unavailableReasons"`
	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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL