Taken over from the join (6ada2089, f1417ab0, 6e162a60, 3a4e62b0, 73fb42d7, ee15a280, a5476f62, 94a4e62a, 33907a81, e519cc87, e8f268c1) and fitted to the final contracts: the seat's events are pull-updated, pull-merged, pull-closed, forge-delete; OwedEnded keeps only the fields §164 names; merge-pull takes no ask. A merge act is performed only as a switched act (§169); from G the gate owes none (§167). Builds: design 47 G §43, §45, §157, §161, §162, §163, §164, §167, §168, §169, §171, §250 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
96 lines
3.4 KiB
Go
96 lines
3.4 KiB
Go
package groups
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// What a person asks of the bridge's performing of what is owed (novox/hq design 47 *What the gate owes the forge*): the
|
|
// tools bridge_owed (its consumers' backlog), bridge_owed_again and bridge_owed_drop write an ask into the state
|
|
// owed-asks and wait for the bridge's answer there, as bridge_group_end_again does, since the bridge is the other program
|
|
// and serves no request subject (ADR 0437 §4).
|
|
const (
|
|
// OwedAsksState is the state of asks about what is owed, one key per kind of ask and act.
|
|
OwedAsksState = "owed-asks"
|
|
// OwedAsksBucket is its bucket on the bus.
|
|
OwedAsksBucket = Module + "_" + OwedAsksState
|
|
)
|
|
|
|
// The kinds of an owed ask.
|
|
const (
|
|
OwedAgain = "again"
|
|
OwedDrop = "drop"
|
|
OwedBacklog = "backlog"
|
|
)
|
|
|
|
// Backlog is one durable consumer of a seat's stream owed as the bridge reads it: the last event applied (its stream
|
|
// sequence), the events waiting and those delivered and not yet acknowledged, and the oldest of them.
|
|
type Backlog struct {
|
|
Stream string `json:"stream"`
|
|
Consumer string `json:"consumer"`
|
|
LastApplied uint64 `json:"last-applied"`
|
|
Waiting uint64 `json:"waiting"`
|
|
Held uint64 `json:"held"`
|
|
Oldest time.Time `json:"oldest,omitzero"`
|
|
OldestSecs int64 `json:"oldest-secs,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// OwedAsk is a value of owed-asks: what was asked (of the act Of said under ID, for again and drop), why, who and when,
|
|
// and once the bridge answered, when, what it came to (the outcome said as owed.ended) and its words; a backlog ask is
|
|
// answered with the consumers' backlog.
|
|
type OwedAsk struct {
|
|
Kind string `json:"kind"`
|
|
Of string `json:"of,omitempty"`
|
|
ID string `json:"id,omitempty"`
|
|
Why string `json:"why,omitempty"`
|
|
By string `json:"by,omitempty"`
|
|
At time.Time `json:"at"`
|
|
Answered time.Time `json:"answered,omitzero"`
|
|
Outcome string `json:"outcome,omitempty"`
|
|
Answer string `json:"answer,omitempty"`
|
|
Backlog []Backlog `json:"backlog,omitempty"`
|
|
}
|
|
|
|
// Done says the bridge answered it.
|
|
func (a OwedAsk) Done() bool { return !a.Answered.IsZero() }
|
|
|
|
// NewOwedAsk is an ask's key and value: a backlog ask under the key backlog, an act's under its kind, its seat and its id
|
|
// (unpadded base64url, as an id may hold what a key may not). A drop says why.
|
|
func NewOwedAsk(kind, of, id, why, by string, at time.Time) (string, []byte, error) {
|
|
a := OwedAsk{Kind: kind, Of: of, ID: id, Why: strings.TrimSpace(why), By: by, At: at.UTC()}
|
|
key := kind
|
|
switch kind {
|
|
case OwedBacklog:
|
|
a.Of, a.ID = "", ""
|
|
case OwedAgain, OwedDrop:
|
|
if of == "" || id == "" {
|
|
return "", nil, fmt.Errorf("an owed act is named by the seat that said it and its id")
|
|
}
|
|
if kind == OwedDrop && a.Why == "" {
|
|
return "", nil, fmt.Errorf("a drop says why")
|
|
}
|
|
key = kind + "." + of + "." + base64.RawURLEncoding.EncodeToString([]byte(id))
|
|
default:
|
|
return "", nil, fmt.Errorf("no owed ask %q: again, drop or backlog", kind)
|
|
}
|
|
v, err := json.Marshal(a)
|
|
return key, v, err
|
|
}
|
|
|
|
// DecodeOwedAsk reads a value of owed-asks.
|
|
func DecodeOwedAsk(data []byte) (OwedAsk, error) {
|
|
var a OwedAsk
|
|
if err := json.Unmarshal(data, &a); err != nil {
|
|
return a, err
|
|
}
|
|
switch a.Kind {
|
|
case OwedAgain, OwedDrop, OwedBacklog:
|
|
return a, nil
|
|
}
|
|
return a, fmt.Errorf("no owed ask %q", a.Kind)
|
|
}
|