GR5: a module's sent store is read by the callers and consumers it is for
Derived at every pass from the catalog graph's manifests and the seat definitions' callers, never from a manifest field. Builds: design 47 G §59, §124, §442 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -192,6 +192,8 @@ type Inputs struct {
|
||||
EventSubjects map[string]map[string]string
|
||||
// AcceptCallers are, by pulled-by-kind seat and queue, the seats that may ask it (acceptCallersOf).
|
||||
AcceptCallers map[string]map[string][]string
|
||||
// VerbCallers are, by seat and verb, the callers each verb of the definitions admits (verbCallersOf).
|
||||
VerbCallers map[string]map[string][]string
|
||||
// Defined are the events a seat definition of the seat list defines, by local name, and mesh-sdk's finding events: the
|
||||
// events a `*` emitter before them reaches each declaring module's own stream for (design 47 G §386, §474, §644).
|
||||
Defined map[string]bool
|
||||
@@ -373,6 +375,7 @@ func ReadInputs(r Reader, cache *Manifests) Inputs {
|
||||
in.Chunked = buscompose.ChunkVerbsOf(d.Definitions())
|
||||
in.EventSubjects = eventSubjectsOf(d.Definitions())
|
||||
in.AcceptCallers = acceptCallersOf(d.Definitions())
|
||||
in.VerbCallers = verbCallersOf(d.Definitions())
|
||||
in.Defined = definedEventsOf(d.Definitions())
|
||||
}
|
||||
nodes := map[string]bool{}
|
||||
|
||||
@@ -102,6 +102,9 @@ func records(in Inputs, union bool) (buscompose.Records, []Gap) {
|
||||
d.Watches, d.Consumes, d.Hears, keptHeard = keptOutOfHearing(in.Kept, d.Watches, d.Consumes, d.Hears)
|
||||
d.HearsAStream = d.HearsAStream || keptHeard
|
||||
d.Reads, d.ReadsObjects, d.ReadsRefused = readsOfObjects(m.Module, node, d.Reads, d.ReadsRefused, declared, values)
|
||||
// The sent stores of the modules it is for (design 47 G §59, §124), read from the catalog graph's manifests and the
|
||||
// definitions' callers.
|
||||
d.ReadsObjects = append(d.ReadsObjects, sentStoresRead(m, in.Manifests, in.VerbCallers)...)
|
||||
// Its bindings as kept (bindings.go): the same source its membership's `bindings` is written from in this pass,
|
||||
// so a grant and its binding are written together, never one before the other (issue 1966, the kind of issue 249).
|
||||
d.Bindings = in.Bindings[m.Module]
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package grants
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/seatdef"
|
||||
)
|
||||
|
||||
// **A module's sent store is read by the modules it is for** (design 47 G §59, §124): the grants part derives, from the
|
||||
// catalog graph and the seat definitions, never from a manifest field, a standing read of `<module>_sent-objects` for the
|
||||
// callers the module's seat definition names for the verbs it serves, and for the consumers whose `consumes` declare its
|
||||
// events. The grant follows the catalog graph, so it is composed with each module's declaration at every pass.
|
||||
|
||||
// verbCallersOf is, by seat and verb, the callers each verb of the definitions admits (a verb naming none takes its class's
|
||||
// default).
|
||||
func verbCallersOf(defs map[string]seatdef.Definition) map[string]map[string][]string {
|
||||
out := map[string]map[string][]string{}
|
||||
for _, d := range defs {
|
||||
for _, v := range d.Verbs {
|
||||
if out[d.Seat] == nil {
|
||||
out[d.Seat] = map[string][]string{}
|
||||
}
|
||||
out[d.Seat][v.Name] = v.EffectiveCallers()
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// admits says whether a verb's callers name the module: `invokers` (every module whose manifest invokes the verb), the module
|
||||
// itself, or a seat it claims.
|
||||
func admits(callers []string, m catalogue.Manifest) bool {
|
||||
for _, c := range callers {
|
||||
switch {
|
||||
case c == seatdef.CallerInvokers, c == seatdef.CallerModulePrefix+m.Module:
|
||||
return true
|
||||
}
|
||||
if seat, ok := strings.CutPrefix(c, seatdef.CallerSeatPrefix); ok && slices.ContainsFunc(m.Claims, func(x catalogue.Claim) bool { return x.Name == seat }) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// sentStoresRead are the bus names of the sent stores the module m reads: of each module whose event it consumes (the module's
|
||||
// own, or the seat a module claims), and of each module claiming a seat whose verb m invokes where the verb's callers admit m.
|
||||
// Never m's own store, which its binding grants it.
|
||||
func sentStoresRead(m catalogue.Manifest, all map[string]catalogue.Manifest, callers map[string]map[string][]string) []string {
|
||||
claimants := func(name string) []string {
|
||||
var out []string
|
||||
if _, ok := all[name]; ok {
|
||||
out = append(out, name)
|
||||
}
|
||||
for module, x := range all {
|
||||
if slices.ContainsFunc(x.Claims, func(c catalogue.Claim) bool { return c.Name == name }) {
|
||||
out = append(out, module)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
var stores []string
|
||||
for _, c := range m.Consumes {
|
||||
name, _, ok := strings.Cut(strings.TrimPrefix(c, "seat:"), ".")
|
||||
if !ok || name == "*" {
|
||||
continue
|
||||
}
|
||||
for _, module := range claimants(name) {
|
||||
stores = append(stores, catalogue.SentObjectsName(module))
|
||||
}
|
||||
}
|
||||
for _, in := range m.Invokes {
|
||||
seat, verb, ok := catalogue.SeatInvoke(in)
|
||||
if !ok || !admits(callers[seat][verb], m) {
|
||||
continue
|
||||
}
|
||||
for module, x := range all {
|
||||
if slices.ContainsFunc(x.Claims, func(c catalogue.Claim) bool { return c.Name == seat }) {
|
||||
stores = append(stores, catalogue.SentObjectsName(module))
|
||||
}
|
||||
}
|
||||
}
|
||||
stores = slices.DeleteFunc(stores, func(s string) bool { return s == catalogue.SentObjectsName(m.Module) })
|
||||
slices.Sort(stores)
|
||||
return slices.Compact(stores)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package grants
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/seatdef"
|
||||
)
|
||||
|
||||
// The sent stores a module reads follow the catalog graph (design 47 G §59, §124): the consumers of a module's events and the
|
||||
// callers a seat's verb admits, and no other module.
|
||||
func TestSentStoresRead(t *testing.T) {
|
||||
holder := catalogue.Manifest{Module: "shop", Claims: []catalogue.Claim{{Name: "orders-seat"}}}
|
||||
all := map[string]catalogue.Manifest{"shop": holder, "bank": {Module: "bank"}, "plain": {Module: "plain"}}
|
||||
callers := map[string]map[string][]string{"orders-seat": {
|
||||
"place": {"seat:billing", "module:plain"}, "list": seatdef.DefaultCallers(seatdef.ClassRead), "wipe": seatdef.DefaultCallers(seatdef.ClassAct)}}
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
m catalogue.Manifest
|
||||
want []string
|
||||
}{
|
||||
{"consumes a module's event", catalogue.Manifest{Module: "bank", Consumes: []string{"shop.sold"}}, []string{"shop_sent-objects"}},
|
||||
{"consumes a seat's event", catalogue.Manifest{Module: "bank", Consumes: []string{"orders-seat.sold"}}, []string{"shop_sent-objects"}},
|
||||
{"invokes a verb that names its seat", catalogue.Manifest{Module: "bank", Claims: []catalogue.Claim{{Name: "billing"}},
|
||||
Invokes: []string{"seat:orders-seat.place"}}, []string{"shop_sent-objects"}},
|
||||
{"invokes a verb that names it", catalogue.Manifest{Module: "plain", Invokes: []string{"seat:orders-seat.place"}}, []string{"shop_sent-objects"}},
|
||||
{"invokes a read, its callers include invokers", catalogue.Manifest{Module: "bank", Invokes: []string{"seat:orders-seat.list"}}, []string{"shop_sent-objects"}},
|
||||
{"invokes an act no one names it for", catalogue.Manifest{Module: "bank", Invokes: []string{"seat:orders-seat.wipe"}}, nil},
|
||||
{"invokes a verb that names others", catalogue.Manifest{Module: "bank", Invokes: []string{"seat:orders-seat.place"}}, nil},
|
||||
{"declares neither", catalogue.Manifest{Module: "bank"}, nil},
|
||||
{"its own store is its binding's", holder, nil},
|
||||
} {
|
||||
if got := sentStoresRead(c.m, all, callers); !slices.Equal(got, c.want) {
|
||||
t.Errorf("%s: %v, want %v", c.name, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user