GATE8: gate_source and gate_source_again, the gate's source revisions read and said again
Builds: design 47 G §83, §84, §93
This commit is contained in:
@@ -267,6 +267,7 @@ func tools(h *Holder, l *listening) []stdio.Tool {
|
||||
}},
|
||||
}
|
||||
list = append(list, recordTools(h, meshhandactsclient.Recorder{Ask: stdio.Ask})...)
|
||||
list = append(list, sourceTools(h, meshhandactsclient.Recorder{Ask: stdio.Ask})...)
|
||||
// A verb that changes a delivery asks for a look at what it makes possible (react.go).
|
||||
for i := range list {
|
||||
run := list[i].Run
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package main
|
||||
|
||||
// The gate's tools over its source revisions (design 47 G §83, §84, §93): gate_source reads every branch's source revision
|
||||
// whole, and gate_source_again says source-moved once more for one branch, recorded first in the hand-act log of the seat
|
||||
// mesh-hand-acts, so a part that missed it reads the branch's source again.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient"
|
||||
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery"
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
"git.novox.be/novox/mesh-sdk/go/keys"
|
||||
)
|
||||
|
||||
// SourceAll is the seat verb source's answer for every branch the gate keeps a source revision of, each as the verb answers
|
||||
// it for that branch, or why the branches cannot be read.
|
||||
func (h *Holder) SourceAll() map[string]any {
|
||||
list := h.SourceRevision("")
|
||||
if list.Error != "" {
|
||||
return map[string]any{"error": list.Error}
|
||||
}
|
||||
out := make([]seat.SourceAnswer, 0, len(list.Branches))
|
||||
for _, b := range list.Branches {
|
||||
out = append(out, h.SourceRevision(b))
|
||||
}
|
||||
return map[string]any{"branches": out}
|
||||
}
|
||||
|
||||
// SourceAgain says source-moved again for one branch with the revision its key holds now, after the hand-act log took it:
|
||||
// nothing is said when the log did not take it, or the branch has no source key.
|
||||
func (h *Holder) SourceAgain(branch, why, by string, acts handActs) (map[string]any, error) {
|
||||
if strings.TrimSpace(branch) == "" || strings.TrimSpace(why) == "" {
|
||||
return nil, errors.New("say the branch and why its source-moved is said again")
|
||||
}
|
||||
if h.Source == nil || h.SaySeat == nil {
|
||||
return nil, errors.New("this gate keeps no source or says no events")
|
||||
}
|
||||
held, rev, err := h.Source.Read(branch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if held == nil {
|
||||
return nil, fmt.Errorf("the gate keeps no source revision of the branch %s", branch)
|
||||
}
|
||||
if acts == nil {
|
||||
return nil, errors.New("the hand-act log cannot be reached here, so nothing was said")
|
||||
}
|
||||
if _, err := acts.Record(meshhandactsclient.Request{What: "said source-moved again for the branch " + branch,
|
||||
Why: why, Cause: "gate_source_again", Verb: "source-again"}); err != nil {
|
||||
return nil, fmt.Errorf("the hand act was not recorded through mesh-hand-acts, so nothing was said: %w", err)
|
||||
}
|
||||
at := h.Now().UTC()
|
||||
escaped := keys.EscapeName(branch)
|
||||
id := "source-moved." + escaped + "." + strconv.FormatUint(rev, 10) + ".again." + strconv.FormatInt(at.UnixNano(), 10)
|
||||
if err := h.SaySeat(seat.SubjectSourceMoved(escaped),
|
||||
seat.SourceMovedEvent{Branch: branch, Group: held.Group, Revision: int64(rev), At: &at}, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]any{"said": "source-moved", "branch": branch, "revision": rev, "by": by}, nil
|
||||
}
|
||||
|
||||
// sourceTools are the source's tools; acts is the hand-act log.
|
||||
func sourceTools(h *Holder, acts handActs) []stdio.Tool {
|
||||
return []stdio.Tool{
|
||||
{Name: "gate_source",
|
||||
Description: "Every branch's source revision the gate keeps, each whole: its repositories with their trunk branch and head, " +
|
||||
"the group that moved it last and the key's revision. Read, never written.",
|
||||
Run: func(map[string]any) (any, error) { return h.SourceAll(), nil }},
|
||||
{Name: "gate_source_again",
|
||||
Description: "Say source-moved again for one branch, with the revision its source key holds now, for a part that missed it; " +
|
||||
"recorded in the hand-act log of the seat mesh-hand-acts first.",
|
||||
Input: map[string]any{"type": "object", "properties": map[string]any{
|
||||
"branch": str("the watched branch"), "why": str("why it is said again, in plain words")},
|
||||
"required": []string{"branch", "why"}},
|
||||
Run: func(a map[string]any) (any, error) {
|
||||
return h.SourceAgain(strArg(a, "branch"), strArg(a, "why"), stdio.CallerOf(a), acts)
|
||||
}},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
seat "git.novox.be/novox/mesh-delivery/contracts/go/seats/meshdelivery"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type branchSource struct{ memSource }
|
||||
|
||||
func (b *branchSource) Branches() ([]string, error) {
|
||||
var out []string
|
||||
for k := range b.vals {
|
||||
out = append(out, k)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Design 47 G §83, §84: gate_source answers every branch's source revision whole; gate_source_again says source-moved once
|
||||
// more for one branch with a message id of its own, only after the hand-act log took it.
|
||||
func TestTheSourceToolsReadEveryBranchAndSayItAgainAfterTheLog(t *testing.T) {
|
||||
src := &branchSource{}
|
||||
for _, b := range []string{"main", "feat"} {
|
||||
_, _ = src.Set(b, func(*Source) (Source, bool) {
|
||||
return Source{Group: "g-" + b, Heads: map[string]string{"novox/app": "c1"}}, true
|
||||
})
|
||||
}
|
||||
type said struct{ subject, id string }
|
||||
var events []said
|
||||
now := time.Date(2026, 11, 12, 4, 0, 0, 0, time.UTC)
|
||||
h := &Holder{Now: func() time.Time { return now }, Source: src,
|
||||
SaySeat: func(subject string, body any, id string) error {
|
||||
events = append(events, said{subject, id})
|
||||
return nil
|
||||
}}
|
||||
all := h.SourceAll()["branches"].([]seat.SourceAnswer)
|
||||
if len(all) != 2 {
|
||||
t.Fatalf("gate_source answered %+v", all)
|
||||
}
|
||||
acts := &handActsFake{refuse: errors.New("log down")}
|
||||
if _, err := h.SourceAgain("main", "a part missed it", "me", acts); err == nil || len(events) != 0 {
|
||||
t.Fatalf("said without the log: %v %v", err, events)
|
||||
}
|
||||
acts.refuse = nil
|
||||
if _, err := h.SourceAgain("main", "", "me", acts); err == nil {
|
||||
t.Fatal("said with no reason")
|
||||
}
|
||||
if _, err := h.SourceAgain("nothing", "x", "me", acts); err == nil {
|
||||
t.Fatal("said for a branch with no source")
|
||||
}
|
||||
if _, err := h.SourceAgain("main", "a part missed it", "me", acts); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(acts.took) != 1 || acts.took[0].Cause != "gate_source_again" || len(events) != 1 ||
|
||||
!strings.HasSuffix(events[0].subject, ".event.source-moved.main") || !strings.Contains(events[0].id, ".again.") {
|
||||
t.Fatalf("log %+v events %+v", acts.took, events)
|
||||
}
|
||||
}
|
||||
@@ -349,7 +349,9 @@
|
||||
"gate_record",
|
||||
"gate_record_end",
|
||||
"gate_record_repair",
|
||||
"gate_records"
|
||||
"gate_records",
|
||||
"gate_source",
|
||||
"gate_source_again"
|
||||
],
|
||||
"config": {
|
||||
"record-retention": {
|
||||
|
||||
Reference in New Issue
Block a user