Let a process read the router's record of its module's own ask (issue 1189)
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
stdio "git.novox.be/novox/mesh-sdk/go"
|
||||
)
|
||||
|
||||
// Ask reads the router's record of one of this process's module's own asks, under its own module and no
|
||||
// other's (novox/hq ADR 0436 clause 4, ADR 0259 §3): by direct get on bucket, the same subject the tool
|
||||
// runner reads (mesh-tools node-tools/internal/bus/seat.go ~300-345):
|
||||
//
|
||||
// $JS.API.DIRECT.GET.KV_<bucket>.$KV.<bucket>.<module>.<id>
|
||||
//
|
||||
// bucket is the router's records bucket as the caller's own manifest names it (operator-channel's is
|
||||
// messenger_asks in the controller's own test fixtures) — never hard-coded to one bucket here, since a
|
||||
// process may read records of more than one seat's router. A missing, deleted or purged key is nil, not
|
||||
// an error.
|
||||
func (c *Conn) Ask(bucket, id string) (*stdio.StateEntry, error) {
|
||||
if err := checkAskID(id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
subject := "$JS.API.DIRECT.GET.KV_" + bucket + ".$KV." + bucket + "." + c.account.Module + "." + id
|
||||
ctx, cancel := context.WithTimeout(context.Background(), StateTimeout)
|
||||
defer cancel()
|
||||
reply, err := c.nc.RequestWithContext(ctx, subject, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading %s's ask %s from %s: %w", c.account.Module, id, bucket, err)
|
||||
}
|
||||
if status := reply.Header.Get("Status"); status == "404" {
|
||||
return nil, nil
|
||||
} else if status != "" {
|
||||
return nil, fmt.Errorf("the record %s.%s could not be read: %s %s", c.account.Module, id, status,
|
||||
reply.Header.Get("Description"))
|
||||
}
|
||||
if op := reply.Header.Get("KV-Operation"); op == "DEL" || op == "PURGE" {
|
||||
return nil, nil
|
||||
}
|
||||
var revision uint64
|
||||
if _, err := fmt.Sscan(reply.Header.Get("Nats-Sequence"), &revision); err != nil {
|
||||
return nil, fmt.Errorf("the record %s.%s carries no readable revision: %w", c.account.Module, id, err)
|
||||
}
|
||||
return &stdio.StateEntry{Key: c.account.Module + "." + id, Value: askValueOf(reply.Data), Revision: revision}, nil
|
||||
}
|
||||
|
||||
// checkAskID refuses an id holding a dot or a wildcard before anything is sent: either would name a
|
||||
// different key under the direct-get subject's own dot-joined path, never this one (novox/hq issue 1189
|
||||
// AC2).
|
||||
func checkAskID(id string) error {
|
||||
if id == "" || strings.ContainsAny(id, ".*>") {
|
||||
return fmt.Errorf("%q is not an ask id: an ask id holds no dot and no wildcard, naming one key and "+
|
||||
"no other", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// askValueOf is a record's raw bytes as JSON, quoting them when they are not already valid JSON
|
||||
// (mesh-tools node-tools/internal/bus/state.go:351-357, valueOf).
|
||||
func askValueOf(raw []byte) json.RawMessage {
|
||||
if len(raw) == 0 || !json.Valid(raw) {
|
||||
b, _ := json.Marshal(string(raw))
|
||||
return b
|
||||
}
|
||||
return json.RawMessage(append([]byte(nil), raw...))
|
||||
}
|
||||
Reference in New Issue
Block a user