NATS3 (part, branch-independent): which module sends, and a catalog entry read for the bus module
Sends reads a claim of a seat whose definition has a verb or an event, or an event of its own; AsksOf reads an entry's bus-state asks and claims. The follower's branch and the wiring wait on the design's answer. Builds: design 47 G §344, §433 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
catalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
|
||||
"nats-tools/internal/keeper"
|
||||
)
|
||||
|
||||
// The keeper reads a module's asks from the module's entry in the catalog half of its revision view, not from the
|
||||
// indexer's event (design 47 G §344, §433).
|
||||
|
||||
// Asks is what one catalog graph entry says of a module for the keeper: what its manifest asks of the bus as an accepted
|
||||
// verdict, the seats it claims, and whether it declares an event of its own (the facts keeper.Sends reads).
|
||||
type Asks struct {
|
||||
Accepted keeper.Accepted
|
||||
Claims []string
|
||||
EmitsOwn bool
|
||||
}
|
||||
|
||||
// AsksOf reads one entry. An entry whose manifest is not accepted asks nothing of the bus now, as an accepted verdict of
|
||||
// no entries does; the digest it carries is of the entries it asks, so an entry changed in nothing the bus module reads
|
||||
// is not applied again.
|
||||
func AsksOf(e catalog.CatalogEntry) (Asks, error) {
|
||||
if e.Module == "" {
|
||||
return Asks{}, fmt.Errorf("a catalog entry names no module")
|
||||
}
|
||||
raw, err := json.Marshal(e.Manifest)
|
||||
if err != nil {
|
||||
return Asks{}, fmt.Errorf("the manifest of %s is not readable: %w", e.Module, err)
|
||||
}
|
||||
var m catalogue.Manifest
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
return Asks{}, fmt.Errorf("the manifest of %s is not readable: %w", e.Module, err)
|
||||
}
|
||||
state := m.BusState
|
||||
if e.Verdict != "" && e.Verdict != "accepted" {
|
||||
state = nil
|
||||
}
|
||||
sum := sha256.Sum256(mustJSON(state))
|
||||
out := Asks{Accepted: keeper.Accepted{Module: e.Module, Digest: hex.EncodeToString(sum[:]), BusState: state},
|
||||
EmitsOwn: len(m.Emits) > 0 || len(e.Streams) > 0}
|
||||
for _, c := range m.Claims {
|
||||
out.Claims = append(out.Claims, c.Name)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func mustJSON(v any) []byte {
|
||||
b, _ := json.Marshal(v)
|
||||
return b
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
catalog "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshcatalog"
|
||||
)
|
||||
|
||||
func TestAsksOf(t *testing.T) {
|
||||
m := map[string]any{"module": "m", "claims": []any{map[string]any{"name": "seat-a"}}, "emits": []any{"done"}}
|
||||
a, err := AsksOf(catalog.CatalogEntry{Module: "m", Verdict: "accepted", Manifest: m})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(a.Claims) != 1 || a.Claims[0] != "seat-a" || !a.EmitsOwn || a.Accepted.Module != "m" || a.Accepted.Digest == "" {
|
||||
t.Errorf("read %+v", a)
|
||||
}
|
||||
b, _ := AsksOf(catalog.CatalogEntry{Module: "m", Manifest: map[string]any{"module": "m"}})
|
||||
if b.EmitsOwn || len(b.Claims) != 0 {
|
||||
t.Errorf("an entry with neither: %+v", b)
|
||||
}
|
||||
if b.Accepted.Digest != a.Accepted.Digest {
|
||||
t.Errorf("the digest is of the bus-state asked alone: %s vs %s", b.Accepted.Digest, a.Accepted.Digest)
|
||||
}
|
||||
if _, err := AsksOf(catalog.CatalogEntry{}); err == nil {
|
||||
t.Error("an entry with no module was read")
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
occ "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient"
|
||||
oc "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel"
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/seatdef"
|
||||
)
|
||||
|
||||
// fakeBus holds objects by stream, as a bus would, and refuses what a test tells it to.
|
||||
@@ -794,3 +795,31 @@ func TestTheKeeperMakesWhatAVerdictAsksForAndSaysIt(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSends(t *testing.T) {
|
||||
defs := map[string]seatdef.Definition{
|
||||
"verbed": {Verbs: []seatdef.Verb{{Name: "go"}}},
|
||||
"evented": {Events: []seatdef.Message{{Name: "done"}}},
|
||||
"included": {Includes: []string{"part"}},
|
||||
"bare": {},
|
||||
}
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
claims []string
|
||||
own bool
|
||||
want bool
|
||||
}{
|
||||
{"a seat with a verb", []string{"verbed"}, false, true},
|
||||
{"a seat with an event", []string{"evented"}, false, true},
|
||||
{"a seat that includes parts", []string{"included"}, false, true},
|
||||
{"a seat with neither", []string{"bare"}, false, false},
|
||||
{"a seat the list does not hold", []string{"unknown"}, false, false},
|
||||
{"an event of its own", nil, true, true},
|
||||
{"nothing claimed", nil, false, false},
|
||||
{"one of two claims sends", []string{"bare", "verbed"}, false, true},
|
||||
} {
|
||||
if got := Sends(c.claims, c.own, defs); got != c.want {
|
||||
t.Errorf("%s: Sends = %v, want %v", c.name, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"git.novox.be/novox/mesh-sdk/go/buscompose"
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/membership"
|
||||
"git.novox.be/novox/mesh-sdk/go/seatdef"
|
||||
)
|
||||
|
||||
// The kinds of bus object an entry is made of: a stream, a key-value bucket (stream KV_<name>) and an object store
|
||||
@@ -48,6 +49,22 @@ const (
|
||||
SentAgeDays = 7
|
||||
)
|
||||
|
||||
// Sends says whether a module sends (design 47 G §344): it claims a seat whose definition has a verb or an event, or it
|
||||
// declares an event of its own (emitsOwn). claims are the seats its catalog graph entry claims, defs the seat list's
|
||||
// definitions by seat; a claimed seat the list does not hold counts for nothing. A definition that includes parts takes
|
||||
// their verbs and events whole (seatdef.Resolve), so one counts as having some.
|
||||
func Sends(claims []string, emitsOwn bool, defs map[string]seatdef.Definition) bool {
|
||||
if emitsOwn {
|
||||
return true
|
||||
}
|
||||
for _, c := range claims {
|
||||
if d, ok := defs[c]; ok && (len(d.Verbs) > 0 || len(d.Events) > 0 || len(d.Includes) > 0) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Settings are one bus object's settings, as asked and as held: what check compares.
|
||||
type Settings struct {
|
||||
// MaxBytes is its cap in bytes; MaxAgeSeconds its age, 0 for none.
|
||||
|
||||
Reference in New Issue
Block a user