Documentation
¶
Overview ¶
Package workspace is the working-copy half of configuration versioning (the history half is core/revision): each administrator has one workspace, edited by them (in any console) and by their agents, and committed as a revision. A workspace holds items (what a codec makes of a configuration: an entity, a role, the settings...) that may be invalid while edited, like code half written. Values merge three ways (see Merge): changes made on both sides to the same value are conflicts, which keep the local value and block committing until resolved; nothing is overwritten silently.
The package knows items, not what they mean: the rules a merge needs (how list elements are keyed, which text is optional) come in a Schema.
Example (PathString) ¶
fmt.Println(Path{{Field: "grants"}, {Element: "orders"}, {Field: "actions"}})
Output: grants[orders].actions
Index ¶
- Constants
- Variables
- func IsMissing(v any) bool
- func NewID() string
- type Change
- type Conflict
- type Item
- type LogEntry
- type MemoryStore
- func (m *MemoryStore) PutWorkspace(_ context.Context, w Workspace, expected int64, entry LogEntry) (Workspace, error)
- func (m *MemoryStore) Workspace(_ context.Context, username string) (Workspace, bool, error)
- func (m *MemoryStore) WorkspaceLog(_ context.Context, username string, limit int) ([]LogEntry, error)
- type Note
- type Open
- type Path
- type Ref
- type Schema
- func (s Schema) Canonical(v any) string
- func (s Schema) Carry(pending, found []Conflict, now, base Snapshot, source *Source) []Conflict
- func (sc Schema) Changes(a, b Snapshot) []Ref
- func (s Schema) Diff(a, b any) []Change
- func (s Schema) GetAt(root any, path Path) any
- func (s Schema) Merge(base, local, next any, path Path) (any, []ValueConflict)
- func (s Schema) OpenConflicts(now, base Snapshot, cs []Conflict) []Open
- func (s Schema) Replay(old, local, next Snapshot, source Source, pinned bool) (Snapshot, []Conflict)
- func (s Schema) Resolve(now, base Snapshot, cs []Conflict, id string, theirs bool) (Snapshot, []Conflict, bool)
- func (s Schema) Same(a, b any) bool
- func (s Schema) SetAt(root any, path Path, value any) any
- func (s Schema) View(now, base Snapshot, c Conflict) (Open, bool)
- type Snapshot
- type Source
- type Step
- type Store
- type ValueConflict
- type Workspace
Examples ¶
Constants ¶
const ( SourcePublished = "published" SourceDraft = "draft" SourceWorkspace = "workspace" )
The kinds of sources.
Variables ¶
var ErrChanged = errors.New("workspace: changed since it was read")
ErrChanged: the workspace was written since it was read.
var Missing any = missing{}
Missing is a value that is not there.
Functions ¶
Types ¶
type Conflict ¶
type Conflict struct {
ID string `json:"id"`
Ref Ref `json:"ref"`
Path Path `json:"path"`
Source Source `json:"source"`
Pinned bool `json:"pinned,omitempty"`
// Theirs (pinned only), TheirsGone when that side removed it.
Theirs any `json:"theirs,omitempty"`
TheirsGone bool `json:"theirsGone,omitempty"`
}
Conflict is a value of an item two sides changed differently. The workspace keeps its own value; the other side's is the base's (a published revision's) or, when pinned, the one held here.
type LogEntry ¶
type LogEntry struct {
Version int64 `json:"version"`
At time.Time `json:"at"`
By string `json:"by"`
// Op is what wrote it (edit, apply, update, merge, resolve, reset,
// commit, checkpoint); Items what changed.
Op string `json:"op"`
Items []Ref `json:"items"`
Note string `json:"note,omitempty"`
}
LogEntry records a write of a workspace.
type MemoryStore ¶
type MemoryStore struct {
// contains filtered or unexported fields
}
MemoryStore keeps workspaces in memory, for tests and single-process use.
func NewMemoryStore ¶
func NewMemoryStore(now func() time.Time) *MemoryStore
NewMemoryStore returns an empty MemoryStore. A nil now uses time.Now.
func (*MemoryStore) PutWorkspace ¶
func (m *MemoryStore) PutWorkspace(_ context.Context, w Workspace, expected int64, entry LogEntry) (Workspace, error)
PutWorkspace implements Store.
func (*MemoryStore) WorkspaceLog ¶
func (m *MemoryStore) WorkspaceLog(_ context.Context, username string, limit int) ([]LogEntry, error)
WorkspaceLog implements Store.
type Note ¶
Note says where changes came from: a plan an agent applied or a draft taken in, with what its author said.
type Path ¶
type Path []Step
Path is a sequence of steps; empty is the whole value.
type Schema ¶
type Schema struct {
// Lists are the lists whose elements are keyed, by the pattern of their
// elements ("fields[]"), with the property keying them ("name"); any
// other list is one value.
Lists map[string]string
// Unset are the values whose zero (empty text, false, null) means unset,
// equal to no value at all.
Unset map[string]bool
}
Schema holds the rules the merge needs of the values it merges, by path from an item's root as Path.Pattern writes it ("fields[].mask": the mask of any element of fields). A rule applies at its path only, never to a property of the same name elsewhere.
func (Schema) Canonical ¶
Canonical is the canonical JSON of an item: keys sorted, unset values dropped.
func (Schema) Carry ¶
Carry keeps the conflicts not resolved before (pending) next to those just found: a new one replaces those on or under its value (a removed item replaces the conflicts on its values); one on or under a pending conflict still standing is left to it. A pending conflict against the base (unpinned) stands, after an update, against the newest base (source).
func (Schema) Diff ¶
Diff lists the values that differ from a to b, at the depth the merge compares them (objects by key, keyed lists by element, a group of elements sharing a key as a whole), in a stable order.
func (Schema) Merge ¶
func (s Schema) Merge(base, local, next any, path Path) (any, []ValueConflict)
Merge merges base → local and base → next, the values at path.
func (Schema) OpenConflicts ¶
OpenConflicts are the conflicts that stand in now.
func (Schema) Replay ¶
func (s Schema) Replay(old, local, next Snapshot, source Source, pinned bool) (Snapshot, []Conflict)
Replay re-applies the changes from old to local onto next: an item only local changed takes local's value, others next's; a value both changed differently keeps local's and is returned as a conflict against source (pinned: next's value held in the conflict).
func (Schema) Resolve ¶
func (s Schema) Resolve(now, base Snapshot, cs []Conflict, id string, theirs bool) (Snapshot, []Conflict, bool)
Resolve resolves conflict id: mine keeps the workspace's value, theirs takes the other side's (removing it when removed there). Its choice decides the values under it on its side too; what now matches the other side needs no choice. It returns the workspace and the conflicts left, or false when id is not an open conflict.
func (Schema) Same ¶
Same reports whether two items mean the same, regardless of key order or unset values.
func (Schema) SetAt ¶
SetAt returns root with the value at path replaced, or removed when value is Missing; missing objects on the way are created, and a missing list element is appended.
func (Schema) View ¶
View is c as it stands in the workspace now, against base (its base revision's items): its path (a path through a list element that has since become a group stops at the group), the workspace's value and the other side's; false when there is nothing left to choose, because the workspace holds the other side's value already (for instance typed in by hand) or removed what holds it (the item, a list element or object on the way).
type Snapshot ¶
type Snapshot []Item
Snapshot is a configuration as items, in order. Lookups by ref walk it; an operation that looks many up indexes it first (Index), and one that changes many items applies them at once (Apply): a configuration may hold thousands of items.
func (Snapshot) Apply ¶
Apply returns s with each ref of order set to its value in values: replaced where it is, appended when absent (in that order), or removed when the value is Missing. It walks s once.
type Source ¶
type Source struct {
Kind string `json:"kind"`
// ID is the revision or draft; By who wrote, for a workspace write.
ID string `json:"id,omitempty"`
By string `json:"by,omitempty"`
}
Source is where the other side of a conflict comes from: a revision published since the workspace's base (published), a draft taken in (draft), or a write of the same workspace made meanwhile (workspace: by another console or an agent).
type Step ¶
type Step struct {
Field string `json:"field,omitempty"`
Element string `json:"element,omitempty"`
}
Step is a step into a value: an object property, or the element of a list with a key.
type Store ¶
type Store interface {
// Workspace is username's workspace; false when none is kept.
Workspace(ctx context.Context, username string) (Workspace, bool, error)
// PutWorkspace writes w while the kept one is at version expected (0:
// none), recording entry with the new version, in one transaction; it
// returns w as written, or ErrChanged.
PutWorkspace(ctx context.Context, w Workspace, expected int64, entry LogEntry) (Workspace, error)
// WorkspaceLog is username's log, newest first; limit <= 0 is all.
WorkspaceLog(ctx context.Context, username string, limit int) ([]LogEntry, error)
}
Store keeps workspaces and their write logs.
type ValueConflict ¶
type ValueConflict struct {
Path Path
// Mine is the local value, kept; Missing when removed locally.
Mine any
// Theirs is the other side's value; Missing when removed there.
Theirs any
}
ValueConflict is a value both sides changed differently.
type Workspace ¶
type Workspace struct {
Username string
// Base is the revision it builds on; Items its content, nil the base's.
Base int64
Items Snapshot
Conflicts []Conflict
Notes []Note
// Taken are drafts taken in; Checkpoint the draft last saved from it,
// at version CheckpointAt (the workspace holds it while still there).
Taken []int64
Checkpoint int64
CheckpointAt int64
Version int64
UpdatedAt time.Time
// UpdatedBy is who wrote last: the administrator or agent:<token name>.
UpdatedBy string
}
Workspace is an administrator's workspace as kept.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package workspacetest is the conformance suite every workspace.Store implementation runs: in memory and on each configuration store database.
|
Package workspacetest is the conformance suite every workspace.Store implementation runs: in memory and on each configuration store database. |