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_.$KV... // // 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...)) }