The grants part's old build wrote its wrapper for every membership, a held-back one too, with the unpublished composition inside; the new part never rewrites such a key, so the decoder took the held-back membership as given. It now reads a wrapper only when its published says published or unchanged. The codec's rows join the round-trip test (mesh-sdk is over its unit budget). Review round 1 of issue 774's fix.
70 lines
3.0 KiB
Go
70 lines
3.0 KiB
Go
package membership
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// The grants part's state memberships (buscompose.GrantsMembershipsState) holds, at `<node>.<module>`, the bare
|
|
// membership as the assignments stream carries it. The writer and every reader use this one codec, so the two sides
|
|
// cannot drift: until 2026-10-30 the grants part wrote its own wrapper ({node, module, digest, membership, published,
|
|
// retired}) there while the tool runner read the value as a bare membership, and served nothing (issue 774).
|
|
|
|
// EncodeGrantsMembership is the value of a membership's key in the grants part's memberships: the bare membership.
|
|
func EncodeGrantsMembership(m Membership) []byte {
|
|
raw, _ := json.Marshal(m) // a Membership holds nothing json cannot write
|
|
return raw
|
|
}
|
|
|
|
// DecodeGrantsMembership reads a value of the grants part's memberships (BareGrantsMembership) as a Membership.
|
|
func DecodeGrantsMembership(raw []byte) (Membership, error) {
|
|
var m Membership
|
|
body, err := BareGrantsMembership(raw)
|
|
if err != nil {
|
|
return m, err
|
|
}
|
|
err = json.Unmarshal(body, &m)
|
|
return m, err
|
|
}
|
|
|
|
// BareGrantsMembership is the bare membership a value of the grants part's memberships holds, for a reader that
|
|
// decodes it into a type of its own. It accepts the bare membership and, for the transition, the wrapper the grants
|
|
// part wrote before (a top-level object whose "membership" is an object), answering the membership inside it only when
|
|
// its "published" says "published" or "unchanged". A value that is not a JSON object, a wrapper of a membership held
|
|
// back or saying nothing of its publishing, or a membership naming no node or no module, is an error, never an empty
|
|
// or unpublished membership.
|
|
func BareGrantsMembership(raw []byte) (json.RawMessage, error) {
|
|
var probe struct {
|
|
Membership json.RawMessage `json:"membership"`
|
|
Published *string `json:"published"`
|
|
}
|
|
if err := json.Unmarshal(raw, &probe); err != nil {
|
|
return nil, fmt.Errorf("not a membership: %w", err)
|
|
}
|
|
body := raw
|
|
if inner := bytes.TrimSpace(probe.Membership); len(inner) > 0 {
|
|
if inner[0] != '{' {
|
|
return nil, errors.New("not a membership: its membership is no object")
|
|
}
|
|
// The old wrapper was written for a membership held back too, its composed and unpublished membership inside:
|
|
// only one published (or unchanged) is the stream's, so any other is refused and the reader keeps the stream's.
|
|
switch {
|
|
case probe.Published == nil:
|
|
return nil, errors.New("not a published membership: the wrapper says nothing of its publishing")
|
|
case *probe.Published != "published" && *probe.Published != "unchanged":
|
|
return nil, fmt.Errorf("not a published membership: the wrapper says %q", *probe.Published)
|
|
}
|
|
body = inner
|
|
}
|
|
var m Membership
|
|
if err := json.Unmarshal(body, &m); err != nil {
|
|
return nil, fmt.Errorf("not a membership: %w", err)
|
|
}
|
|
if m.Node == "" || m.Module == "" {
|
|
return nil, errors.New("not a membership: it names no node or no module")
|
|
}
|
|
return json.RawMessage(body), nil
|
|
}
|