diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/catalog.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/catalog.go index 62df17fb..7248b5ab 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/catalog.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/catalog.go @@ -4,7 +4,6 @@ import ( "fmt" stdio "git.novox.be/novox/mesh-sdk/go" - "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/delivery" "git.novox.be/novox/mesh-delivery/sdk/graph" @@ -12,30 +11,6 @@ import ( "mesh-delivery-indexer/internal/index" ) -// CatalogTools are the seat mesh-catalog's four verbs as tools named ., with the descriptions and inputs the -// seat's contract gives them (novox/hq ADR 0604 §1, §2), answered from the indexer's own states. They are served once the -// indexer's manifest claims the seat, in the release after L3's (the lead's decision): until then main serves them not. -func CatalogTools(c index.CatalogRead) []stdio.Tool { - seat, ok := catalogue.SeatNamed(catalogue.CatalogSeat) - if !ok { - return nil - } - str := func(args map[string]any, k string) string { s, _ := args[k].(string); return s } - runs := map[string]func(map[string]any) (any, error){ - "modules": func(map[string]any) (any, error) { return c.CatalogModulesAnswer() }, - "module": func(a map[string]any) (any, error) { return c.CatalogModuleAnswer(str(a, "module")) }, - "provides": func(a map[string]any) (any, error) { return c.CatalogProvidesAnswer(str(a, "provision")) }, - "dependents": func(a map[string]any) (any, error) { return c.CatalogDependentsAnswer(str(a, "module")) }, - } - var out []stdio.Tool - for _, v := range seat.Serves { - if run, ok := runs[v.Name]; ok { - out = append(out, stdio.Tool{Name: catalogue.CatalogSeat + "." + v.Name, Description: v.Description, Input: v.Input, Run: run}) - } - } - return out -} - // catalogRead reads the indexer's modules, manifests and graph states for the seat's verbs. func catalogRead(modules, manifests, nodes stdio.KeptState) index.CatalogRead { return index.CatalogRead{ @@ -97,7 +72,7 @@ func catalogRead(modules, manifests, nodes stdio.KeptState) index.CatalogRead { } } -// catalogTools are the seat's verbs over the indexer's own states, as main serves them once the indexer claims the seat. +// catalogTools are the seat's verbs over the indexer's own states, served beside the indexer's own tools, since the indexer's manifest claims the seat (ADR 0604 §2). func catalogTools() []stdio.Tool { - return CatalogTools(catalogRead(stdio.State("modules"), stdio.State("manifests"), stdio.State(graph.State))) + return index.CatalogTools(catalogRead(stdio.State("modules"), stdio.State("manifests"), stdio.State(graph.State))) } diff --git a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/main.go b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/main.go index cfc4ff71..d9cb1aa0 100644 --- a/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/main.go +++ b/modules/mesh-delivery-indexer/cmd/mesh-delivery-indexer-tools/main.go @@ -190,7 +190,7 @@ func heldCounts(s stdio.KeptState) func() (index.OldSectionsCount, error) { } func main() { - if err := stdio.Serve("", Tools(askTakeIn(stdio.State(index.TakeInsState), 25*time.Second), time.Now, stdio.State(index.VerdictsState), heldCounts(stdio.State(index.OldSectionsState)))); err != nil { + if err := stdio.Serve("", append(Tools(askTakeIn(stdio.State(index.TakeInsState), 25*time.Second), time.Now, stdio.State(index.VerdictsState), heldCounts(stdio.State(index.OldSectionsState))), catalogTools()...)); err != nil { fmt.Fprintf(os.Stderr, "[mesh-delivery-indexer-tools] %v\n", err) os.Exit(1) } diff --git a/modules/mesh-delivery-indexer/internal/index/catalog.go b/modules/mesh-delivery-indexer/internal/index/catalog.go index 33807d48..c9da450b 100644 --- a/modules/mesh-delivery-indexer/internal/index/catalog.go +++ b/modules/mesh-delivery-indexer/internal/index/catalog.go @@ -7,6 +7,7 @@ import ( "slices" "strings" + stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/delivery" @@ -147,3 +148,27 @@ func (c CatalogRead) graphAndModules() (graph.Graph, map[string]delivery.Module, } return g, mods, nil } + +// CatalogTools are the seat mesh-catalog's four verbs as tools named ., with the descriptions and inputs the +// seat's contract gives them (novox/hq ADR 0604 §1, §2), answered from the indexer's own states. They are served once the +// indexer's manifest claims the seat, in the release after L3's (the lead's decision): until then main serves them not. +func CatalogTools(c CatalogRead) []stdio.Tool { + seat, ok := catalogue.SeatNamed(catalogue.CatalogSeat) + if !ok { + return nil + } + str := func(args map[string]any, k string) string { s, _ := args[k].(string); return s } + runs := map[string]func(map[string]any) (any, error){ + "modules": func(map[string]any) (any, error) { return c.CatalogModulesAnswer() }, + "module": func(a map[string]any) (any, error) { return c.CatalogModuleAnswer(str(a, "module")) }, + "provides": func(a map[string]any) (any, error) { return c.CatalogProvidesAnswer(str(a, "provision")) }, + "dependents": func(a map[string]any) (any, error) { return c.CatalogDependentsAnswer(str(a, "module")) }, + } + var out []stdio.Tool + for _, v := range seat.Serves { + if run, ok := runs[v.Name]; ok { + out = append(out, stdio.Tool{Name: catalogue.CatalogSeat + "." + v.Name, Description: v.Description, Input: v.Input, Run: run}) + } + } + return out +} diff --git a/modules/mesh-delivery-indexer/internal/index/catalog_test.go b/modules/mesh-delivery-indexer/internal/index/catalog_test.go index 03c0b68c..dead2776 100644 --- a/modules/mesh-delivery-indexer/internal/index/catalog_test.go +++ b/modules/mesh-delivery-indexer/internal/index/catalog_test.go @@ -93,6 +93,19 @@ func catalogVerbsAnswerFromTheStates(t *testing.T) { } return true } + // The tools bundle serves the four verbs as mesh-catalog., answering from the same states. + var names []string + for _, tool := range CatalogTools(c) { + names = append(names, tool.Name) + if tool.Name == "mesh-catalog.modules" { + if got, err := tool.Run(nil); err != nil || !strings.Contains(string(answer(got, nil)), `"module":"avahi"`) { + t.Errorf("mesh-catalog.modules answered %v, %v", got, err) + } + } + } + if !slices.Equal(names, []string{"mesh-catalog.modules", "mesh-catalog.module", "mesh-catalog.provides", "mesh-catalog.dependents"}) { + t.Errorf("the tools bundle serves %v", names) + } raw := answer(c.CatalogModulesAnswer()) if got, want := keysAt(t, raw, "modules"), capturedKeys(t, "catalog_modules.json", "modules"); !contains(got, want) { t.Errorf("modules' fields %v, want the old catalog's %v", got, want) diff --git a/modules/mesh-delivery-indexer/manifest_test.go b/modules/mesh-delivery-indexer/manifest_test.go index 7569b44c..4911041b 100644 --- a/modules/mesh-delivery-indexer/manifest_test.go +++ b/modules/mesh-delivery-indexer/manifest_test.go @@ -12,6 +12,7 @@ import ( "strings" "testing" + "git.novox.be/novox/mesh-sdk/go/catalogue" "git.novox.be/novox/mesh-sdk/go/delivery" "git.novox.be/novox/mesh-delivery/sdk/graph" @@ -38,9 +39,14 @@ type artifact struct { } type manifest struct { - Module string `json:"module"` - Version string `json:"version"` - Slug string `json:"slug"` + Module string `json:"module"` + Version string `json:"version"` + Slug string `json:"slug"` + Claims []struct { + Name string `json:"name"` + Scope string `json:"scope"` + Serves []string `json:"serves"` + } `json:"claims"` Tools []string `json:"tools"` Emits []string `json:"emits"` Consumes []string `json:"consumes"` @@ -122,8 +128,14 @@ func TestManifestDeclarations(t *testing.T) { // 1238/AC1, 1444 (reads the build if want := []string{"indexer_take_in", "indexer_old_sections", "indexer_verdicts", "indexer_judge"}; !reflect.DeepEqual(m.Tools, want) { t.Errorf("tools = %v, want %v", m.Tools, want) } - // No streams, no bus, no seat (ADR 0437 §4); it says no event. - for _, k := range []string{"streams", "logs", "claims", "provides", "requires", "uses", "defines-seats", "seat", "seats", "bus"} { + // ADR 0604 §2: it holds the seat mesh-catalog, serving its four verbs from its tools bundle (which stays without a + // request subject). + if len(m.Claims) != 1 || m.Claims[0].Name != catalogue.CatalogSeat || m.Claims[0].Scope != catalogue.ScopeMesh || + !slices.Equal(m.Claims[0].Serves, []string{"modules", "module", "provides", "dependents"}) { + t.Errorf("claims = %+v, want mesh-catalog with its four verbs", m.Claims) + } + // No streams, no bus, no other seat (ADR 0437 §4); it says no event. + for _, k := range []string{"streams", "logs", "provides", "requires", "uses", "defines-seats", "seat", "seats", "bus"} { if _, ok := top[k]; ok { t.Errorf("the manifest declares %q: the indexer has none", k) } diff --git a/modules/mesh-delivery-indexer/module.json b/modules/mesh-delivery-indexer/module.json index 11fac677..3fa9e8a7 100644 --- a/modules/mesh-delivery-indexer/module.json +++ b/modules/mesh-delivery-indexer/module.json @@ -2,6 +2,18 @@ "module": "mesh-delivery-indexer", "version": "1", "slug": "indexer", + "claims": [ + { + "name": "mesh-catalog", + "scope": "mesh", + "serves": [ + "modules", + "module", + "provides", + "dependents" + ] + } + ], "tools": [ "indexer_take_in", "indexer_old_sections",