diff --git a/modules/nats/cmd/nats-tools/consumer.go b/modules/nats/cmd/nats-tools/consumer.go index 93cb8e2ab..ccaa14555 100644 --- a/modules/nats/cmd/nats-tools/consumer.go +++ b/modules/nats/cmd/nats-tools/consumer.go @@ -11,7 +11,6 @@ import ( "encoding/json" "errors" "fmt" - "regexp" "strings" "time" @@ -110,10 +109,10 @@ const ( maxAsks = 3000 ) -// eventStream is a stream that keeps events: the shared EVENTS and a seat's kept events SEAT__EVENTS. Only these show -// a body; every other stream holds calls and replies, asks, mail, dead letters, records or logs, whose bodies carry what -// is not an event's to show. It fails closed: a stream not named here shows sequence, subject, time and size only. -var eventStream = regexp.MustCompile(`^(EVENTS|SEAT_[A-Z0-9]+(_[A-Z0-9]+)*_EVENTS)$`) +// bodyStream is the one stream that shows a body: the shared EVENTS, which holds every event, the seats' kept event streams +// (SEAT__) being sourced from it. Every other stream holds calls and replies, asks, mail, dead letters, records +// or logs, whose bodies are not an event's to show: it answers sequence, subject, time and size only. +const bodyStream = "EVENTS" // MessageRow is one message of the answer. type MessageRow struct { @@ -272,7 +271,7 @@ func readTools(jsz func() ([]byte, error), open MsgOpen) []stdio.Tool { return Consumer(doc, arg(a, "stream"), arg(a, "consumer")) }}, {Name: "nats_stream_messages", - Description: "The messages a stream keeps for one subject (a filter with * or > is allowed), newest first: sequence, subject, time and size of each, and the body only when asked for, no larger than 4 KiB, and only on an event stream (EVENTS or a seat's kept events); on every other stream (asks, calls and replies, mail, dead letters, records, logs) only sequence, subject, time and size. Bounded by count (default 20, at most 200) and bytes (default 1 MiB); says when it stopped before the oldest the stream keeps. Read on the bus module's own credential. Refuses the streams of a module's kept state (KV_*, OBJ_*): their values are read through their owners' tools; this tool is for lost events.", + Description: "The messages a stream keeps for one subject (a filter with * or > is allowed), newest first: sequence, subject, time and size of each, and the body only when asked for, no larger than 4 KiB, and only on the stream EVENTS (every kept event is there); on every other stream (kept events, asks, calls and replies, mail, dead letters, records, logs) only sequence, subject, time and size. Bounded by count (default 20, at most 200) and bytes (default 1 MiB); says when it stopped before the oldest the stream keeps. Read on the bus module's own credential. Refuses the streams of a module's kept state (KV_*, OBJ_*): their values are read through their owners' tools; this tool is for lost events.", Input: map[string]any{"stream": str("the stream, e.g. EVENTS"), "subject": str("a subject or filter, e.g. mesh.mod.mesh-delivery-indexer.event.view-changed"), "count": num("at most this many (default 20, at most 200)"), "bytes": num("at most this many bytes of messages (default 1048576)"), "body": map[string]any{"type": "boolean", "description": "carry the body of each message of at most 4 KiB"}}, @@ -296,8 +295,8 @@ func readTools(jsz func() ([]byte, error), open MsgOpen) []stdio.Tool { bytes, _ := a["bytes"].(float64) body, _ := a["body"].(bool) bodyHidden := "" - if body && !eventStream.MatchString(stream) { - body, bodyHidden = false, stream+" is not an event stream (EVENTS or a seat's kept events): its bodies are not shown, only sequence, subject, time and size" + if body && stream != bodyStream { + body, bodyHidden = false, stream+" is not the stream EVENTS: its bodies are not shown, only sequence, subject, time and size" } ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() diff --git a/modules/nats/cmd/nats-tools/consumer_test.go b/modules/nats/cmd/nats-tools/consumer_test.go index e17017bb0..b8adf93d5 100644 --- a/modules/nats/cmd/nats-tools/consumer_test.go +++ b/modules/nats/cmd/nats-tools/consumer_test.go @@ -147,14 +147,17 @@ func TestTheReadToolsAreServedAndRefuseBadArguments(t *testing.T) { if rows := ev.(map[string]any)["messages"].([]MessageRow); len(rows) != 1 || rows[0].Body != "body-50" { t.Fatalf("an event stream showed no body: %v", ev) } - for _, s := range []string{"EVENTS", "SEAT_NODE_BUILD_GO_EVENTS"} { - if !eventStream.MatchString(s) { - t.Fatalf("%s is an event stream", s) + seat := strings.Replace(consumerJsz, `"name":"EVENTS"`, `"name":"SEAT_node-engine_running"`, 1) + for _, tl := range readTools(func() ([]byte, error) { return []byte(seat), nil }, + func(context.Context) (MsgGet, func(), error) { + return fake(map[uint64]string{50: "a.b"}, nil), func() {}, nil + }) { + if tl.Name != "nats_stream_messages" { + continue } - } - for _, s := range []string{"SEAT_CHANNEL", "ASKS", "MAIL", "DEAD_LETTERS", "LOG_X", "SEAT_X_worker"} { - if eventStream.MatchString(s) { - t.Fatalf("%s is not an event stream", s) + o, err := tl.Run(map[string]any{"stream": "SEAT_node-engine_running", "subject": "a.b", "body": true}) + if err != nil || o.(map[string]any)["messages"].([]MessageRow)[0].Body != "" { + t.Fatalf("a kept event stream showed a body: %v %v", o, err) } } if _, err := by["nats_stream_messages"](map[string]any{"stream": "EVENTS"}); err == nil {